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.
Related
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.
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.
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
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.
This question already has an answer here:
Accessing HttpSession outside of the originally receiving thread
(1 answer)
Closed 6 years ago.
I have a fully-annotation-driven Spring Boot 1.3.5 app which has this asynchronous service which needs to autowire another service bean (And in the future it will need to autowire a repository bean, but I'm not there yet) in order to perform some business logic:
#Service
public class AsyncService {
#Autowired
public HelpingService helpingService;
#Async
public Future<String> doFoo(String someArgument)
throws InterruptedException {
Thread.sleep(3000);
System.out.println("about to do Foo "+someArgument);
String result = "";
try {
result = helpingService.getSomeStuff(someArgument);
}
catch (Exception e) {
e.printStackTrace();
}
return new AsyncResult<String>(hello);
}
}
That method above is being called from a #Controller bean, which has other endpoints (Non-async) that work as expected also using this
#Controller
public class MyController extends BaseController {
#Autowired
HelpingService helpingService;
#Autowired
AsyncService asyncService;
#RequestMapping(method=RequestMethod.GET, value={"/rest/threads/getIp/{jobId}"}, produces={"application/json"})
public ResponseEntity<?> getLog(#PathVariable("jobId") String jobId) throws InterruptedException {
asyncService.doFoo(jobId);
return new ResponseEntity<>(HttpStatus.OK);
}
}
And here's helpingService's implementation (It's an interface), calling any method works perfectly fine when I'm not doing it from the #Async method above:
#Service
#Validated
public class HelpingServiceImpl implements HelpingService {
#Autowired
HttpSession httpSession;
#Value(value="${projName}")
private String projName;
public String getServerAddress(){
AuthRegion region = (AuthRegion) httpSession.getAttribute("region");
if (region != null)
return region.getServerAddress();
else
return null;
}
#Override
public String getSomeStuff(String jobId) {
String responseString = "";
String projName = this.projName;
String serverAddress = getServerAddress(); // Code stops here with an exception
// Some code here that works fine outside this thread
return responseString;
}
}
This is the exception being caught:
about to do Foo (267)
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:309)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:64)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:366)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:361)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307)
at com.sun.proxy.$Proxy96.getAttribute(Unknown Source)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getRundeckServerPort(ThreadServiceImpl.java:45)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getJobExecutionOutput(ThreadServiceImpl.java:65)
at corp.fernandopcg.myapp.service.AsyncService.doFoo(AsyncService.java:40)
at corp.fernandopcg.myapp.service.AsyncService$$FastClassBySpringCGLIB$$7e164220.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
I added (With some changes as I couldn't extend AsyncConfigurer at the same time as SpringBootServletInitializer, and I had to catch an exception not mentiones there) the taskExecutor part to my Application main class as follows, guided by this tutorial which does look similar to what I need, in my opinion
#SpringBootApplication
#EnableAsync
#EnableJpaRepositories(repositoryFactoryBeanClass = DataTablesRepositoryFactoryBean.class)
public class MyApplication extends SpringBootServletInitializer implements AsyncConfigurer{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("SomeRandomLookup-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// TODO Auto-generated method stub
return null;
}
}
Can I tell my #Async service to be able to use other services of the application? Because if that's not possible, I don't really see the use of these threading mechanism.
This is a great illustration of why request-scope injection can be problematic. Your HelpingServiceImpl has a hidden dependency on the request-specific HttpSession, which looks like a field but is actually a proxy that is resolved by Spring on each call to always refer to the "current" request (using a thread-local variable).
The problem is that by making your call #Async, you're separating the HelpingServiceImpl invocation from the request that triggered it, and there's no longer the implicit connection of being on the same thread that would allow it to pull information from the globalish context.
The most straightforward fix is to make your dependencies explicit--instead of having your HelpingServiceImpl grab the region directly off of the HttpSession, pass the region to it as a method parameter.