How to make Aysnc annotation work in Spring Boot? - java

I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}

according to Baeldung:
#Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won't work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
so you can put your async method in a service and use it from there

you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}

不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class

Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.

Related

Proper way to start new thread from service layer using spring

I have a method that will be rarely called. This method collect garbage in db. I don't want to make user to wait for server response so i decided to call this method from new thread from my service layer. i'm using Spring.
Service class:
#Service
#Transactional
public class UploadService {
#Resource(name = "UploadDAO")
private UploadDao uploadDao;
Method that i don't want to wait for
public void collectBlobGarbage(){
Thread th = new Thread(new Runnable() {
#Override
public void run() {
uploadDao.collectBlobGarbage();
}
});
th.start();
}
Is it a good way to do like this?
If you have Spring on your classpath you might as well use #Async
#Async
public CompletableFuture<Void> collectBlobGarbage() throws InterruptedException {
CompletableFuture.completeFuture(uploadDao.collectBlobGarbage());
}
On your main class you need to use #EnableAsync like:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
And you need an executor bean:
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Stackoverflow-");
executor.initialize();
return executor;
}
I think the provided solution can potentially cause a lot of threads on you server.
As an alternative, you can consider using Executors.newSingleThreadExecutor in such a way that you will get an executor service that is limited only to one thread—so you will never create more than one thread—and that is what you need.
Also as you are using Spring, consider configuring a SingleThreadExecutor instantiation as a separate bean—in such a way that you will be able to change the implementation of ExecutorService in the future.

Spring Command Line App hangs after Async method calls complete

I have a Spring Boot Application that uses CommandLineRunner and the Spring #Async annotation to run a method asynchronously. It all works fine, but when all of my threads complete, the application just hangs instead of exiting.
Here is a minimal example of what I have in my application:
Application.java:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationStartup.java:
#Component
public class ApplicationStartup implements CommandLineRunner {
private final AsyncService asyncService;
#Inject
public ApplicationStartup(final AsyncService asyncService) {
this.asyncService = asyncService;
}
#Override
public void run(final String... strings) throws Exception {
//my logic is more complicated than this, but this illustrates my point
for (int i = 0; i < 1000; i++) {
asyncService.runAsyncMethod();
}
}
}
AsyncService.java:
#Service
#Transactional
public class AsyncService {
#Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
ExecutorConfig.java:
#Configuration
public class ExecutorConfig() {
#Bean
public ThreadPoolTaskExecutor asyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(64);
executor.setMaxPoolSize(64);
executor.setQueueCapacity(500);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("Scrub-");
executor.setKeepAliveSeconds(60);
executor.initialize();
return executor;
}
}
All of my threads make the call to runAsyncMethod() and every method call completes successfully, but then the application just hangs.
I tried changing some of the executor settings around. I didn't have the keepAliveSeconds at first, so I thought adding that would fix it, but it still hung after all threads were complete. I changed corePoolSize to 0, and that made the application exit when it was done, but it only used 1 thread the whole time.
Any ideas as to why the application is not exiting with the configuration above?
You missed to join the asynchronous jobs, that's why the run method exits (far) before all threads complete - and the awkward behavior is "more comprehensible".
According to doc, you could join like:
...
CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N];
...
for (int i = 0; i < N; i++) {
myJobs[i] = asyncService.runAsyncMethod();
}
...
CompletableFuture.allOf(myJobs).join();
And your runAsyncMethod() would need to return a CompletableFuture<Void>. To do so, you can just return CompletableFuture.completedFuture(null);
Even if the marked as correct answer is valid. This is not full answer.
Without #EnableAsync and without WEB environment .web(WebApplicationType.NONE) the spring boot app automatically stop once started(as there is nothing to do/wait). So even if you don't do apringApp.close() in your app but only app.run(commandLine), the .close() method call automatically.
But once you added #EnableAsync - the behavior changes, as there might be async work, so app is not stopped once started. And if there is not stopping code, the app remain working (hangs).
For fixing this you need to do 2 things:
in the run method do wait all async work
implicitly call .close()after app started
Sample:
#EnableAutoConfiguration
#EnableAsync
public static class SpringApp extends SpringApplication {
#Bean
public TaskExecutor taskExecutor () {
return new SimpleAsyncTaskExecutor();
}
#Autowired
private Service service;
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event){
CompletableFuture<Void> aggregateFuture = service.doWork();
// avoid exiting this method before all job complected prevents app from hanging
aggregateFuture.join();
}
}
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(SpringApp.class).web(WebApplicationType.NONE);
app.run()
.close(); // <--- THIS!
}

Spring Boot - #Async is ignored

Using Spring Boot 1.5.2.RELEASE and the #Async annotation seems to be ignored.
Have setup the environment like this:
#SpringBootApplication
#EnableAsync
public class Application extends AsyncConfigurerSupport {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("async-task-");
executor.initialize();
return executor;
}
...
... the async method itself:
#Service
public class MyService {
#Async
public Future<Long> asyncTask() throws Exception {
Long test = 1023L;
Thread.sleep(10000);
return new AsyncResult<>(test);
}
}
... now I'm trying to use this:
#RestController
public MyController {
#Autowired
public MyService myService;
#PostMapping("/test")
public ResponseEntity<MyResponse> test() {
return new ResponseEntity<>(
new MyResponse(myService
.asyncTask()
.get()),
HttpStatus.OK);
}
}
... and the controller method still hangs for 10sec instead of to be immediatelly returned.
The #Async method is called from the different object. It's neither private nor transactional one as it mentioned at the similar questions.
How to let the method to be invoked asynchronously?
You should have a look at the
Future#get javadoc:
Waits if necessary for the computation to complete, and then retrieves
its result.
You are transforming your async method to a synchronous call by calling get method.
Thus instead of calling get, just return the Future. Spring MVC supports future as return type:
A ListenableFuture or CompletableFuture/CompletionStage can
be returned when the application wants to produce the value from a
thread pool submission.
Example:
return myService.asyncTask().thenApply(r -> ResponseEntity.ok(new MyResponse(r)));
Your test() function is invoking get() on the Future instance. The documentation for this function states: "Waits if necessary for the computation to complete, and then retrieves its result."
So rather than invoking get(), you likely want to return some sort if ID that the caller can use to retrieve the result at a later time (or switch to a synchronous response).

JUnit-testing a Spring #Async void service method

I have a Spring service:
#Service
#Transactional
public class SomeService {
#Async
public void asyncMethod(Foo foo) {
// processing takes significant time
}
}
And I have an integration test for this SomeService:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
Here is the problem:
as SomeService.asyncMethod(..) is annotated with #Async and
as the SpringJUnit4ClassRunner adheres to the #Async semantics
the testAsyncMethod thread will fork the call someService.asyncMethod(testData) into its own worker thread, then directly continue executing verifyResults(), possibly before the previous worker thread has finished its work.
How can I wait for someService.asyncMethod(testData)'s completion before verifying the results? Notice that the solutions to How do I write a unit test to verify async behavior using Spring 4 and annotations? don't apply here, as someService.asyncMethod(testData) returns void, not a Future<?>.
For #Async semantics to be adhered, some active #Configuration class will have the #EnableAsync annotation, e.g.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
//
}
To resolve my issue, I introduced a new Spring profile non-async.
If the non-async profile is not active, the AsyncConfiguration is used:
#Configuration
#EnableAsync
#EnableScheduling
#Profile("!non-async")
public class AsyncConfiguration implements AsyncConfigurer {
// this configuration will be active as long as profile "non-async" is not (!) active
}
If the non-async profile is active, the NonAsyncConfiguration is used:
#Configuration
// notice the missing #EnableAsync annotation
#EnableScheduling
#Profile("non-async")
public class NonAsyncConfiguration {
// this configuration will be active as long as profile "non-async" is active
}
Now in the problematic JUnit test class, I explicitly activate the "non-async" profile in order to mutually exclude the async behavior:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
#ActiveProfiles(profiles = "non-async")
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
I have done by injecting
ThreadPoolTaskExecutor
and then
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
before verifying results,
it as below:
#Autowired
private ThreadPoolTaskExecutor executor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
verifyResults();
}
If you are using Mockito (directly or via Spring testing support #MockBean), it has a verification mode with a timeout exactly for this case:
https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#22
someAsyncCall();
verify(mock, timeout(100)).someMethod();
Much more capable is the great library Awaitility, which has many options how to handle async assertions. Example:
someAsyncCall();
await().atMost(5, SECONDS)
.untilAsserted(() -> assertThat(userRepo.size()).isEqualTo(1));
In case your method returns CompletableFuture use join method - documentation CompletableFuture::join.
This method waits for the async method to finish and returns the result. Any encountered exception is rethrown in the main thread.
Just to extend the answer by #bastiat, which in my opinion should be considered the correct one, you should also specified the TaskExecutor, if you are working with multiple executors. So you would need to inject the correct one that you wish to wait for. So, let's imagine we have the following configuration class.
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean("myTaskExecutor")
public TaskExecutor myTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(15);
executor.setCoreCapacity(10);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setThreadNamePrefix("MyTaskExecutor-");
executor.initialize();
return executor;
}
// Everything else
}
Then, you would have a service that would look like the following one.
#Service
public class SomeServiceImplementation {
#Async("myTaskExecutor")
public void asyncMethod() {
// Do something
}
// Everything else
}
Now, extending on #bastiat answer, the test would look like the following one.
#Autowired
private SomeService someService;
#Autowired
private ThreadPoolTaskExecutor myTaskExecutor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
this.someService.asyncMethod(testData);
this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
this.verifyResults();
// Everything else
}
Also, I have a minor recommendation that has nothing to do with the question. I wouldn't add the #Transactional annotation to a service, only to the DAO/repository. Unless you need to add it to a specific service method that must be atomic.
Just addition to the above solutions:
#Autowired
private ThreadPoolTaskExecutor pool;
#Test
public void testAsyncMethod() {
// call async method
someService.asyncMethod(testData);
boolean awaitTermination = pool.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
assertThat(awaitTermination).isFalse();
// verify results
}

#Async in Spring doesn't work in Service class?

#Async method in #Service annotated class in standalone Spring Boot application doesn't run asynchronously. What am I doing wrong?
When I run the same method directly from main class (#SpringBootApplication annotated), it works. Example:
Main class
#SpringBootApplication
#EnableAsync
public class Application implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
// here when I call downloadAnSave() it runs asynchronously...
// but when I call downloadAnSave() via downloadAllImages() it does not run asynchronously...
}
}
and my service class (and here asynchronous behavior doesn't work):
#EnableAsync
#Service
public class ImageProcessorService implements IIMageProcessorService {
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}
Calling async method from within the same class would trigger the original method and not the intercepted one.
You need to create another service with the async method, and call it from your service.
Spring creates a proxy for each service and component you create using the common annotations. Only those proxies contain the wanted behavior defined by the method annotations such as the Async. So, calling those method not via the proxy but by the original naked class would not trigger those behaviors.
Workaround is:
#EnableAsync
#Service("ip-service")
public class ImageProcessorService implements IIMageProcessorService {
#Autowired
#Qualifier("ip-service")
ImageProcessorService ipService;
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
ipService.downloadAnSave(productId, imageUrl);
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}
With such approach you call method of proxy, not the class instance.
The same approach can be used with other tools working with proxies, e.g. #Transactional etc.

Categories

Resources