Getting data back from #Async function call - java

Hi I am new to multithreading in java. Can someone please help me with this:
My service:
#Async
public List<String> doSomething(int a){
//Do something
return list;
}
SpringbootApplication:
#SpringBootApplication
#EnableAsync
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
Async config:
#Configuration
#EnableAsync
public class AsyncConfig {
#Bean(name ="taskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("userThread-");
executor.initialize();
return executor;
}
}
Controller:
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public List<String> getAll(){
return service.doSomething(1);
}
}
When I hit this get request from postman it is showing up blank in the response. I understand that my call is going asynchronously and the response is coming back even before the my method is called. Is there any way to see this response by changing some settings in either my postman or spring boot application

If you want to process the request asynchronously but also want the API client to receive the response after it finishes processing such that from the client 's point of view , the request processing still looks like synchronous , the key is to use the Servlet3 asynchronous processing feature.
You do not need to configure it to execute asynchronously in the service level using #Aysnc. Instead configure the controller method to return CompletableFuture. Under the cover , it will trigger Servlet3 's asynchronous request processing which will process the request in another thread besides the HTTP thread that receive the request.
So your codes should look something like:
public class Service {
//No need to add #Async
public List<String> doSomething(int a){
return list;
}
}
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return CompletableFuture.supplyAsync(()->service.doSomething(1));
}
}
For details about the Servlet3 asynchronous request processing supported in spring-mvc , you can refer to this blog series start from this .

You can return CompletableFuture. You will receive http response when CompleteableFuture will be completed.
Service:
#Async
public CompletableFuture<List<String>> doSomething() {
return CompletableFuture.completedFuture(Arrays.asList("1", "2", "3"));
}
Controller:
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return service.getAll();
}

If you want to use async I would split your single request into so called "start task" and "get task result" requests. Your application returns "request id" for "start task" request. Then you use "request id" when performing "get task result". Such a scenario is a common way in the Batch Processing task. If you use Spring, you may be interesting investigating Spring Batch framework, which has Start/Stop/Restart job functionality among others.

Related

Run Spring aspect advice on executing thread

I have a scenario in my springboot application, where I submit tasks into a threadpool for async execution.Now some of the methods inside child execution is part of aspect point advice with #AfterReturn.
I observe that even if processing is done asnyc, my main thread keeps executing the point cut advice from child thread and my service does not return a value until, all child thread finished execution.
Any pointer how to make the advice run on the executing thread itself?
So in short, controller method does not return response until dao method execution and its corresponding point cut is executed.
#Controller
#RequestMapping(value = "/api")
public class SampleController {
#Autowired
SampleService service;
#RequestMapping(value = "/action", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public String action(#RequestBody String request){
service.action(request);
return "Success";
}
}
#Service
public class SampleService{
#Autowired
SampleDao dao;
#Async("threadPoolExecutor")
public void action(String request){
dao.action(request);
}
}
#Repository
public class SampleDao{
public void action(String request){
//do some db things
}
}
#Aspect
#Component
public class SampleAspect{
#AfterReturning(
pointcut = "execution( * com.sample.*.*.SampleDao.action(..))",
returning = "result")
public void audit(JoinPoint joinPoint, Object result) {
//dosome thing
}
}
The #Async on the service method does not mean that it will be submitted to the executor service and then return immediately, but that you can can have several incoming calls to the endpoint which will then be handled concurrently (this is the case per default anyway afaik, #Async is pretty much a marker only).
You can read this guide to see how this can be done properly.
The gist is that your service needs to create (and optionally return) some sort of Future (in the case of the post, CompletableFuture, as in
#Async
void serviceMethod(String request) {
CompletableFuture.submit(() -> dao.action(request));
}
It sounds like you do want to wait for the result to arrive though, so while this will work, I expect you'll run into problems later.

Rest Controller isn't asynchronous

I am trying to create asynchronous REST API. Every call starts service method which takes 2 seconds to finish. I would like to make 20 calls at the same time and start all this methods on different threads so my controller would not be blocked and I could get all responses in about ~ 2 sec.
I tried adding #Asnyc annotation to both Controller, and service, but they were giving me empty response, after adding CompletableFuture as response I am getting proper response but now it is not async and I need to wait about 40 sec for all responses.
#RestController
public class BasicController{
#Autowired
private BasicService basicService;
#Async("asyncExecutor")
#GetMapping("/basic/{owner}/{path}")
public CompletableFuture<InfoDTO>getGitApiInfo(#PathVariable("owner") String owner,
#PathVariable("path") String path) throws InterruptedException {
return basicService.getRepositoryInfo(owner, path);
}
}
#Service
public class BasicService{
#Async("asyncExecutor")
public CompletableFuture<InfoDTO> getRepositoryInfo(String owner, String repoName) throws InterruptedException {
Thread.sleep(2000);
return CompletableFuture.completedFuture(new InfoDTO(owner, repoName);
}
}
#EnableAsync
#Configuration
public class ServicesConfig {
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(500);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
I would like to get 20 responses from this controller in about 2-3 sec using postman runner.

Java rest api that needs to wait before processing

I have a Java rest API which will be used by IOT devices to send data. Each device has a time period (say 15 seconds) to communicate with the API. Within that time period there can be more than one message with the same set of data.
What I want to do is, when the API receive a new message from a device, it wait till the end of the time period and collect the messages received. And process the messages only when the time period is over.
What should I use to collect and process messages for a given time period?
Thanks.
EDIT
Using spring boot.
You should try using an asyncronous endpoint to call to a syncronous REST. You can define what to do after a timeout is reached.
For example, in Spring Boot you could return a Callable and use a TaskExecutor:
#Controller
public class MyController {
#RequestMapping("/endpoint")
public #ResponseBody WebAsyncTask<String> handleRequest (HttpServletRequest request) {
Callable<String> callable = () -> {
return "Callable";
};
ConcurrentTaskExecutor taskExecutor = new ConcurrentTaskExecutor(Executors.newFixedThreadPool(1));
return new WebAsyncTask<>(15000L, taskExecutor, callable);
}
}
You will probably need to add some configuration in Spring for the Task Executor Thread Pool:
#SpringBootApplication
public class AsyncConfigExample {
#Bean
WebMvcConfigurer configurer(){
return new WebMvcConfigurerAdapter(){
#Override
public void configureAsyncSupport (AsyncSupportConfigurer configurer) {
ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor();
t.setCorePoolSize(10);
t.setMaxPoolSize(100);
t.setQueueCapacity(50);
t.setAllowCoreThreadTimeOut(true);
t.setKeepAliveSeconds(120);
t.initialize();
configurer.setTaskExecutor(t);
}
};
}
}
Here is more to read:
Spring MVC: Configuring Asynchronous Request Processing
Spring Boot: Executing asynchronous method backed with a queue

How to make a async REST with Spring?

I'm trying to make a small REST using Spring Boot.
I've never used Spring and used Java a long time ago (Java 7)!
In the last 2 years I have used only Python and C# (but like I said, I already used Java).
So, now, I'm trying to make a REST using async methods, and checked several examples, but still, I don't understand very well the "correct way" to do this.
Looking at the following documentation: http://carlmartensen.com/completablefuture-deferredresult-async, Java 8 has CompletableFuture that I can use with Spring, so, I made the following code:
Service:
#Service
public class UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Repository:
public interface UserRepository extends MongoRepository<User, String> {
#Async
findByEmail(String email);
}
RestController:
#RestController
public class TestController {
private UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
}
}
This code give me the expected output.
Then, looking at another documentation (sorry, I lost the link), I see that Spring accept the following code (which give me the expected output too):
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email);
}
}
Is there a difference between the two methods?
Then, looking at the following guide: https://spring.io/guides/gs/async-method/, there's a #EnableAsync annotation in SpringBootApplication class.
If I include the #EnableAsync annotation and create a asyncExecutor Bean like the code from last link, my application don't return nothing on /test endpoint (only a 200 OK response, but with blank body).
So, my rest is async without the #EnableAsync annotation?
And why when I use #EnableAsync, the response body is blank?
The response body is blank because the #Async annotation is used at findEmail method of UserRepository class, it means that there is no data returned to the following sentence User user = userRepository.findByEmail(email); because findByEmail method is running on other different thread and will return null instead of a List object.
The #Async annotation is enabled when you declare #EnableAsync that is the reason why it only happens when you use #EnableAsync because it activates the #Async of findEmail method to run it on other thread.
The method return userService.findByEmail(email); will return a CompletableFuture object that is created from UserService class.
The difference with the second method call is that thenApplyAsync method will create a totally new CompletableFuture from the previous one that comes from userService.findByEmail(email) and will only return the user object that comes from the first CompletableFuture.
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
If you want to get the expected results just remove the #Async annotation from findByEmail method, and finally add the #EnableAsync Annotation
If you need to clarify ideas of how to use Async methods, lets say that you have to call three methods and each one takes 2 seconds to finish, in a normal scenario you will call them method1, then method2 and finally method3 in that case you entire request will take 6 seconds. When you activate the Async approach then you can call three of them and just wait for 2 seconds instead of 6.
Add this long method to user service:
#Async
public CompletableFuture<Boolean> veryLongMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(true);
}
And call it three times from Controller, like this
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();
CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
return userService.findByEmail(email);
}
Finally measure the time that takes your response, if it takes more than 6 seconds then you are not running Async method, if it takes only 2 seconds then you succeed.
Also see the following documentation: #Async Annotation, Spring async methods, CompletableFuture class
Hope it help.
The Asynchronous child threads start executing very late (around 20 to 30 seconds delay).
I'm using ThreadPoolTaskExecutor() in my main SpringBoot application class. You can also try the same if you consider performance as a factor.

Returning a value from direct channel flow and proceeding with async flow in Spring Integration

I have the following integration configuration in my web app:
#Bean
IntegrationFlow giraFlow() {
return IntegrationFlows.from(
MessageChannels.direct("gira.input"))
.split()
.transform(transformer)
.handle(parserService)
.channel(routerChannel())
.get();
}
#Bean
MessageChannel routerChannel() {
return MessageChannels.queue("routerChannel", 10)
.get();
}
#Bean
IntegrationFlow routerChannelFlow() {
return IntegrationFlows.from(
routerChannel())
.route(p -> p.getKind().name(),
m -> m.suffix("Channel")
.channelMapping(TaskKind.CREATE.name(), "create")
.channelMapping(TaskKind.RELOAD.name(), "reload")
.get();
}
and a gateway:
#MessagingGateway
public interface GW {
#Gateway(requestChannel = "gira.input")
Task gira(Collection<Request> messages);
}
and a parserService
#Service
#Slf4j
public class ParserService {
public Task handle(IssueTrackerTask task) {
log.info("Parser service handling task {}", task);
return task;
}
}
I call the gateway method from Spring MVC controller and I want it to return me a Task object that the parserService returns in it's body method. The important thing is that I want controller to be blocked until it gets the value from the parserService. After it gets this value, I want my integration flow to proceed asynchronously with the routerChannelFlow, so that web controller method would return as fast as possible, and all heavy operations in the routerChannelFlow would be done without blocking controller.
Here's a part of the controller that has this gateway method call:
...
Task gira = gw.gira(messages);
log.info("Result {}", gira);
...
The problem is that log.info is never reached and gira() gateway is blocked forever.
How can I achieve my desired behavior?
P.S. The parserService is actually not needed in my app, this is just what I thought would help me to define a return value for my gateway, but it actually did not help:(
UPDATE
So here's what I got after Gary Russell's comment:
#Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(10);
pool.setMaxPoolSize(10);
pool.setWaitForTasksToCompleteOnShutdown(true);
return pool;
}
#Bean
MessageChannel routerChannel() {
return MessageChannels
.publishSubscribe("routerChannel", executor())
.get();
}
#Bean
IntegrationFlow routerChannelFlow() {
return IntegrationFlows
.from(routerChannel())
.publishSubscribeChannel(s -> s
.subscribe(f -> f
.bridge(null))
.subscribe(process()))
.get();
}
#Bean
IntegrationFlow process() {
return f ->
f.<IssueTrackerTask, String>route(p -> p.getKind().name(),
m -> m.suffix("Channel")
.channelMapping(TaskKind.CREATE.name(), "create")
.channelMapping(TaskKind.RELOAD.name(), "reload")
}
And when I try to use this pipeline, I get the following error Dispatcher has no subscribers for channel 'application:development:9000.dummy'. It's definitely a misconfiguration issue, but I cannot figure out what am I doing wrong.
UPDATE
Changed channel("dummy") to bridge(null).
What is downstream of the create and reload channels?
What do you do with the Task result in the controller (aside from logging it)?
If you don't need a result, change the gateway return to void and add an executor channel downstream of the gateway.
If you want the Task object returned, you need the routerChannel to be a publish/subscribe channel with an executor and two subscribers - a bridge to nowhere (input channel and no output channel) which will return the Task to the gateway, and the router, which will route the Task on a separate thread; the downstream flows from the router must not return a result (the gateway will have long-before stopped waiting for a result).
Instead of adding an executor to routerChannel, you could make the two router channels executor channels instead.
Your last solution is almost there, you just don't need to use .channel("dummy"), but just .bridge(null) in that first subflow subscriber for the .publishSubscribeChannel().
What you need there is just to send message back to the gateway's replyChannel, which is TemporaryReplyChannel in message headers.
The BridgeHandler is the best way do "nothing" but just send message to the appropriate replyChannel from headers.
Also consider to add executor to the channel after .split() if your .transform() and .hanlde() logic is enough heavy.

Categories