Spring Boot - #Async is ignored - java

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).

Related

Send an response 200 from a Spring boot controller for asyncronus processing

I have a scenario:
I've a controller.
My controller is calling the service class.Service class will fetch all the records from data base and post it to kafka.But I don't want my client who is calling the api to wait till the entire data is posted to kafka. I want it to do in a asynchronous way. Client's work is only to invoke the api that's all. We will immediately send 200 response code.
In back end the data fetching from DB and processing to kafka will happen.
Can you please suggest how this can be done.
public class SampleController {
private SampleService sampleService;
#PostMapping("/")
public String handleAsyncResponse(#RequestBody Requestbean bean) {
sampleService.posttokafka(bean);
}
}
Thanks
You can achieve it via #Async way as follows, first define a bean:
#Configuration
public class SThreads {
#Bean
public TaskExecutor concurrentTasks() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
}
}
And the annotate the task with #Async("concurrentTasks")
#Async("concurrentTasks")
public static void myAsyncTask() throws ExecutionException, InterruptedException {
....
}
Then you can call the myAsyncTask and it will not block the caller.

How to assert exceptions in #Async void methods?

I want to assert an exception that should be thrown within an #Async void method.
The following fails, even though I already add a SyncTaskExecutor explicit.
org.opentest4j.AssertionFailedError: Expected RuntimeException to be thrown, but nothing was thrown.
#TestConfiguration
public class SyncTaskExecutorTestConfiguration {
#Bean
#Primary
public TaskExecutor asyncExecutor() {
return new SyncTaskExecutor();
}
}
#SpringBootTest
#Import(SyncTaskExecutorTestConfiguration.class)
public class MyTest {
#Test
public void test() {
assertThrows(RuntimeException.class, () -> service.run());
}
}
#Service
#Async //also #EnableAsync existing on #Configuration class
public class AsyncService {
public void run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
I'm facing the same problem.
bilak's post gave the idea of having my custom AsyncUncaughtExceptionHandler declared with a #Component annotation.
Then, in my custom implmentation of AsyncConfigurer I was injecting my custom AsyncUncaughtExceptionHandler.
In my tests, I used the #MockBean annotation on my custom AsyncUncaughtExceptionHandler, so I was able to verify that the handleUncaughtException was called with the appropriate exception.
Code sample:
AsyncExceptionHandler
#Slf4j
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("Exception while executing with message: {} ", throwable.getMessage());
log.error("Exception happen in {} method ", method.getName());
}
}
CustomAsyncConfigurer
#Configuration
public class CustomAsyncConfigurer implements AsyncConfigurer {
final private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
public TaskExecutorConfiguration(AsyncExceptionHandler asyncExceptionHandler) {
this.asyncExceptionHandler = asyncExceptionHandler;
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsyncThread::");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
My unit test:
class FooServiceTest extends FooApplicationTests {
#MockBean
private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
private FooService fooService;
#Test
void testCreateEnrollmentBioStoreException() throws Exception {
fooService.doBar();
ArgumentCaptor<FooException> argumentCaptor = ArgumentCaptor.forClass(FooException.class);
verify(asyncExceptionHandler, times(1)).handleUncaughtException(argumentCaptor.capture(), any(), any());
FooException exception = argumentCaptor.getValue();
assertEquals("Foo error message", exception.getMessage());
}
}
I'm not sure if this is the right way, but I have a void method that was turned into async, so I didn't want to change the return value just for the tests.
Since the #Async method get executed asynchronously by a thread from asyncExecutor and it is terminated due to RuntimeException which doesn't have any impact on Main thread, the actually Main-Test thread competes successfully with the rest of flow once after it trigger the async call. So i will recommend to use the CompletableFuture to hold the reference of Async process always even it's required or not and truthfully will help in test cases
#Service
#Async
public class AsyncService {
public CompletableFuture<Void> run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
So in the test you can wait for Async thread to complete assert the cause from ExecutionException, Since the get method throws ExecutionException if this future completed exceptionally
CompletableFuture.allOf(wait);
One more note you can refer link for asserting wrapped exceptions
What about using AsyncUncaughtExceptionHandler that will be defined for your AsyncConfigurer?
So basically when you execute your method which throws exception you can verify that exception was handled inside handler? Just an idea, didn't tried this.

How to make Aysnc annotation work in Spring Boot?

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.

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!
}

Categories

Resources