Trying to enable async event handling combining the #Async and #EventListener annotations, but I still see that the listener is running in the publishing thread.
The example you can find here:
#SpringBootApplication
#EnableAsync
class AsyncEventListenerExample {
static final Logger logger = LoggerFactory.getLogger(AsyncEventListenerExample.class);
#Bean
TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
static class MedicalRecordUpdatedEvent {
private String id;
public MedicalRecordUpdatedEvent(String id) {
this.id = id;
}
#Override
public String toString() {
return "MedicalRecordUpdatedEvent{" +
"id='" + id + '\'' +
'}';
}
}
#Component
static class Receiver {
#EventListener
void handleSync(MedicalRecordUpdatedEvent event) {
logger.info("thread '{}' handling '{}' event", Thread.currentThread(), event);
}
#Async
#EventListener
void handleAsync(MedicalRecordUpdatedEvent event) {
logger.info("thread '{}' handling '{}' event", Thread.currentThread(), event);
}
}
#Component
static class Producer {
private final ApplicationEventPublisher publisher;
public Producer(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void create(String id) {
publisher.publishEvent(new MedicalRecordUpdatedEvent(id));
}
#Async
public void asynMethod() {
logger.info("running async method with thread '{}'", Thread.currentThread());
}
}
}
and my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = AsyncEventListenerExample.class)
public class AsyncEventListenerExampleTests {
#Autowired
Producer producer;
#Test
public void createEvent() throws InterruptedException {
producer.create("foo");
//producer.asynMethod();
// A chance to see the logging messages before the JVM exists.
Thread.sleep(2000);
}
}
However in logs I see that both #EventListeners run in the main thread.
2016-05-12 08:52:43.184 INFO 18671 --- [ main] c.z.e.async2.AsyncEventListenerExample : thread 'Thread[main,5,main]' handling 'MedicalRecordUpdatedEvent{id='foo'}' event
2016-05-12 08:52:43.186 INFO 18671 --- [ main] c.z.e.async2.AsyncEventListenerExample : thread 'Thread[main,5,main]' handling 'MedicalRecordUpdatedEvent{id='foo'}' event
The async infrastructure is initialised with #EnableAsync with an asynchronous TaskExecutor.
Not sure what I am doing wrong. Could you help?
Thanks.
Using Spring Boot 1.4.2.M2, so Spring 4.3.0.RC1
There was a regression in Spring Framework 4.3.0.RC1 that leads to that very issue you're having. If you use the SNAPSHOT, your project runs fine.
onTicketUpdatedEvent runs in also main Thread with Spring Framework 4.2.4 Release as follows.
But it runs in SimpleAsyncTaskExecutor if AsyncConfigurer is not implemented.
#EnableAsync(proxyTargetClass = true)
#Component
#Slf4j
public class ExampleEventListener implements AsyncConfigurer {
#Async
#EventListener
public void onTicketUpdatedEvent(TicketEvent ticketEvent) {
log.debug("received ticket updated event");
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
I resolved my issue by configuring task-executor bean as follows.
#Bean(name = "threadPoolTaskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
#Async("threadPoolTaskExecutor")
#EventListener
public void onTicketUpdatedEvent(TicketEvent ticketEvent) {
log.debug("received ticket updated event");
}
Related
Spring Boot 3 has changed context propagation in tracing.
https://github.com/micrometer-metrics/tracing/wiki/Spring-Cloud-Sleuth-3.1-Migration-Guide#async-instrumentation
They deliver now library to this issue. I guess I don't quite understand how it works.
I have created a taskExecutor as in guide.
#Bean(name = "taskExecutor")
ThreadPoolTaskExecutor threadPoolTaskScheduler() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor() {
#Override
protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler);
return ContextExecutorService.wrap(executorService, ContextSnapshot::captureAll);
}
};
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
And I have marked #Async like this:
#Async("taskExecutor")
public void run() {
// invoke some service
}
But context is not propagated to child context in taskExecutor thread.
I was facing the same problem. Pls add this code to the configuration and everything works as expected.
#Configuration(proxyBeanMethods = false)
static class AsyncConfig implements AsyncConfigurer, WebMvcConfigurer {
#Override
public Executor getAsyncExecutor() {
return ContextExecutorService.wrap(Executors.newCachedThreadPool(), ContextSnapshot::captureAll);
}
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new SimpleAsyncTaskExecutor(r -> new Thread(ContextSnapshot.captureAll().wrap(r))));
}
}
I need to create a dedicated thread listening on DatagramSocket.
Old school approach would be to create one during context creation:
#Bean
void beanDef() {
var thread = new Thread(myRunnable);
thread.setDaemon(true);
thread.start();
}
More modern approach would be to create an executor:
#Bean
Executor() {
var executor = Executors.newSingleThreadExecutor();
executor.submit(myRunnable);
}
Which one should I prefer?
Something like this would be a modern way to launch a background thread in Spring:
#Component
public class MySocketListenerLauncher {
private ExecutorService executorService;
#PostConstruct
public void init() {
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("socket-listener-%d").build();
executorService = Executors.newSingleThreadExecutor(factory);
executorService.execute(new Runnable() {
#Override
public void run() {
// ... listen to socket ...
}
});
executorService.shutdown();
}
#PreDestroy
public void shutdown() {
if (executorService != null) {
executorService.shutdownNow();
}
}
}
Better use #Async annotation to create a thread in the background.
The #EnableAsync annotation switches on Spring's ability to run #Async methods in a background thread pool. This class also customizes the used Executor.
#Configuration
#EnableAsync
public class ThreadConfig {
#Bean(name = "specificTaskExecutor")
public TaskExecutor specificTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return executor;
}
}
and in below snippet
#Async("specificTaskExecutor")
public void runFromAnotherThreadPool() {
System.out.println("You function code here");
}
I hope this will help you.
Initially I was using regular java Multithreading using the 'implements' method. However #Autowired does not work when a class is created with new in Spring, so I am trying to change it to using Spring'sAsync method. This is what I have so far. How would I go about adding the threads to the ThreadPoolExecutor?
The class that should create the threads
#Component
public class ScheduledCountyScraper {
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
// #Scheduled(cron = "0 0 */3 * * *")
#EventListener(ApplicationReadyEvent.class)
public void scrapeCountyLinks() {
System.out.println("Scrape county links ran!");
try {
List<String> stateLinks = stateScrapeQueueRepository.getStatesLinks(website);
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
//what to do here?
executor.shutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("---------------------");
}
}
}
The Async class
#Component
#EnableAsync
public class CountyScraper {
volatile private String stateLink;
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
public CountyScraper() {
}
public CountyScraper(String stateLink) {
this.stateLink = stateLink;
}
#Async("countyScraper")
public void run() {
try {
// other code
stateScrapeQueueRepository.updateScrapeTimestamp(stateLink);
countyScrapeRepository.insertCountyLinks(countyLinks, website);
} catch (Exception e) {
e.printStackTrace();
}
}
}
By default Spring uses a SimpleAsyncTaskExecutor to execute async methods. This will by default spawn a new thread for every operation.
To define your own executor for use with async tasks, create a bean that implements the TaskExecutor interface or an Executor bean named "taskExecutor".
If you'd like to have your own custom executor just for this component, you can implement AsyncConfigurer and provide your own executor service:
#Override
public Executor getAsyncExecutor() {
return MY_EXECUTOR;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return MY_EXCEPTION_HANDLER;
}
I have a requirement to use same thread for multiple request in async spring.
Here is the code which i am using :
Here is the Executor configuration file
#Configuration
#EnableAsync
public class ExecutorConfig {
#Bean(name = "threadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("threadPoolExecutor-");
executor.initialize();
return executor;
}
}
Here is the service file
public interface QueueService {
public void createQueue();
public void sendData();
}
Here is the Service Implementation
#Service
public class QueueServiceImpl implements QueueService {
#Override
#Async("threadPoolExecutor")
public void createQueue() {
System.out.println(Thread.currentThread().getName());
}
#Override
#Async("threadPoolExecutor")
public void sendData() {
// Need to use the same thread which created the queue
// Thread.currentThread() should be same for both methods
System.out.println(Thread.currentThread().getName());
}
}
Here is the controller class
#RestController
public class Controller {
#Autowired
QueueService queueService;
#RequestMapping(value="/create", method = RequestMethod.GET)
public void createQueue()
{
queueService.createQueue();
}
#RequestMapping(value="/send", method = RequestMethod.GET)
public void sendDataToQueue()
{
queueService.sendData();
}
}
Here is the Spring boot main application
#EnableAsync
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Please let me know how to use #Async in 2 separate request to do the job done by same thread.
I have configured two different thread pools, one for #Scheduled and other for #Async. However, I notice that the thread-pool for #Async is not being used.
Here is the Scheduler configuration
#Configuration
#EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-sched-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
Here is the Configuration for Async
#Configuration
#EnableAsync
public class AppConfig {
#Bean(name = "asyncTaskExecutor")
public TaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("my-async-pool-");
executor.initialize();
return executor;
}
}
Here is how I invoke them
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
asyncMethod();
}
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
Here are the logs
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message from schedule"}
{"thread":"my-sched-pool-1","level":"INFO","description":"here is the message from async"}
As you can notice, both logs are having same pool of that scheduler. but I expect to see the second one to come from async
If you call #Async methods from the same class they are declared you are effectively bypassing Spring's proxy mechanism and that is why your example is not working. Try calling the method from a separate class annotated with #Service or any of the other #Component types.
#Service
SomeScheduledClass {
private final SomeAsyncClass someAsyncClass;
public SomeScheduledClass(SomeAsyncClass someAsyncClass) {
this.someAsyncClass = someAsyncClass;
}
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
someAsyncClass.asyncMethod();
}
}
#Service
SomeAsyncClass {
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
}