For my studies, I'm working on a Spring Boot REST API. I'm supposed to reduce the execution time of the code when it received a request. So, I thought that make the code asynchronous would be a good idea. But, unfortunately, I face some problems with Spring for this, and despite the few hours of research online to find a solution, I didn't find anything. Let me explain :
To optimize my code, I decided to use #Async Spring Annotation. For this, I created an AsyncConfiguration class who looks like this :
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsynchThread-");
executor.initialize();
return executor;
}
}
Usually, the asyncExecutor() method returns the Executor class or sub-class that must be used by Spring for async calls. In this case, it's a ThreadPoolTaskExecutor. And the majority of my code is annotated with #Async to use my asyncExecutor, like this :
#Async("asyncExecutor")
public CompletableFuture<User> calculateRewards(User user) {
return CompletableFuture.supplyAsync(() -> {
logger.info("Calculating Reward for user : " + user.getUserName());
List<VisitedLocation> userLocations = user.getVisitedLocations();
List<Attraction> attractions = gps.getAllAttraction();
for(VisitedLocation visitedLocation : userLocations) {
for(Attraction attraction : attractions) {
if(user.getUserRewards().stream().filter(r -> r.attraction.attractionName.equals(attraction.attractionName)).count() == 0) {
if(nearAttraction(visitedLocation, attraction)) {
user.addUserReward(new UserReward(visitedLocation, attraction, reward.getAttractionRewardPoints(attraction.attractionId, user.getUserId())));
}
}
}
}
return user;
});
}
But, here's the point : when I run the code, Spring DON'T use my asyncExecutor() bean. How do I know that ? First, when I call an annotated method, in the terminal, here's what I see :
2022-10-27 00:26:24.688 INFO 41436 --- [onPool-worker-4] c.T.T.service.TourGuideMainService : Calculating Reward for user : internalUser919
The "[onPool-worker-4]" is the Thread name, or at least the end of the Thread name. But it's not supposed to be named like that. If you look at my asyncExecutor() method above, you can see that there is a executor.setThreadNamePrefix("AsynchThread-");. If the code was working as intended, the Thread should be called "AsynchThread-4", but It's not.
Secondly, I decided to run my code in Debug mode, and I went into the Debug menu of VS Code, and I dicovered two things :
1 - When I run my stress test who make 1000 calls of calculateRewards() simultaneously, there is only 11 Threads created. Considering the execution time of the calculateRewards() method AND the fact that the Executor have its maxPoolSize by default (i.e. Integer.MAX_VALUE), there should be more than 11 Threads ;
2 - The entire name of the Threads when they are created is "[ForkJoinPool.commonPool-worker-4]" ;
It seems that Spring is using the ForkJoinPool class to create Threads, and it don't ever consider the configuration of my Executor. I have no idea of why it's doing that, I never used ForkJoinPool at all, and like I said, I didn't find anything when searching about that online.
So, why Spring is using ForkJoinPool instead of ThreadPoolTaskExecutor for async methods in my code ? And most important : how can I address that ?
(I hope that it's understandable...)
EDIT 1 : During some random testing, I found that, if Spring seems to use some ForkJoinPool Threads to execute my code, it create a "AsynchThread" anyway but don't use it. That's even more confusing... -_-"
The problem here is that you are using CompletableFuture.supplyAsync to produce your CompletableFuture.
This method will fork off a task running in the ForkJoinPool.commonPool() and execute your Supplier in that task.
Since spring will supply the executor and run your entire method asynchronously, you shouldn't need to use the supplyAsync function with a lambda. Instead your async method should look something like this (inside of your service):
#Service
public class MyService {
...
#Async("asyncExecutor")
public CompletableFuture<User> calculateRewards(User user) {
logger.info("Calculating Reward for user : " + user.getUserName());
List<VisitedLocation> userLocations = user.getVisitedLocations();
List<Attraction> attractions = gps.getAllAttraction();
for(VisitedLocation visitedLocation : userLocations) {
for(Attraction attraction : attractions) {
if(user.getUserRewards().stream().filter(r -> r.attraction.attractionName.equals(attraction.attractionName)).count() == 0) {
if(nearAttraction(visitedLocation, attraction)) {
user.addUserReward(new UserReward(visitedLocation, attraction, reward.getAttractionRewardPoints(attraction.attractionId, user.getUserId())));
}
}
}
}
return CompletableFuture.completedFuture(user);
}
}
If you autowire your service into a CommandLineRunner that executes your method, you can use this runner to see that spring will handle executing your method asynchronously using the Executor you have defined in your configuration.
For example:
#Component
public class Runner implements CommandLineRunner {
private final MyService service;
public Runner(MyService service) {
this.service = service;
}
#Override
public void run(String... args) throws Exception {
CompletableFuture<User> user1 = service.calculateRewards(new User("user1"));
CompletableFuture<User> user2 = service.calculateRewards(new User("user2"));
CompletableFuture<User> user3 = service.calculateRewards(new User("user3"));
CompletableFuture.allOf(user1,user2,user3).join();
}
}
Related
In my spring boot application, There is one scheduler class which execute in every 30 mins and fetch around 20000 records and then insert record one by one in database and then convert that inserted record in XML and send to client. But this process takes too much time to completed 20000 records and records is increasing day by day.
So now, We have to implement asynchronous execution programming in this code so that multiple threads can perform operation instead of one by one records. For example : There are 4 threads and list of item size is 6 then thread_1 will take (0) index object to perform, thread_2 will take (1) index object to perform, thread_3 will take (2) index object to perform, thread_4 will take (3) index object to perform, thread_1 will take (4) index object to perform, thread_2 will take (5) index object to perform
Something like that
For this case, How to implement asynchronous execution
#Service
class ItemService
{
#autowired
private DemoDao dao;
#Scheduled(fixedRate=30000)
public void scheduler(){
try {
List<Item> itemList = dao.getItems();
saveAndSend(itemList);
} catch(Exception e) {
e.printStack();
}
}
public void saveAndSend(List<Item> itemList) throws Exception {
for(int i = 0; i < itemList.size(); i++){
if(itemList.get(i).isDone){
dao.save(itemList.get(i));
int flag = convertInXMLAndSendToTeam(itemList.get(i));
if(flag == 1){
dao.audit(itemList.get(i), "XEQ");
}
}
}
}
}
Please help.
Try putting a name, it works for me like this
#Async("ThreadPoolTaskExecutor")
In SprintBootApplication put the following
#EnableAsync
and then
#Bean("ThreadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(30);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("Async-");
executor.setTaskDecorator(new APMTaskDecorator());
return executor;
}
If you use #Async and #Scheduled in same class the async methods are still executed synchronously.
#Async generates a proxy class which encapsulated the original method. So #Async works only if you invoke the method externally. Hence the #Scheduled on the same class is still executed synchronously.
The simpliest solution would be to move the #Scheduled methods to a sperate class.
#Service
public class ItemService {
#Async
public void saveAndSend() {
}
}
#Service
public class ItemSchedulerService {
private final ItemService itemService;
public ItemSchedulerService(ItemService itemService) {
this.itemService = itemService;
}
#Scheduled("...")
public void schedule() {
itemService.saveAndSend();
}
}
Assume there is a bean MyTask with one method, which can throw an exception. I would like to invoke the method asynchronously (background routine), then check periodically (i.e. each minute) what is its status (in progress, completed or failed) and each time make a decision whether it should be started again or not.
#Component
class MyTask {
public void execute(){
if (System.currentTimeMillis() % 5 == 0){
throw new RuntimeException("Failed");
} else {
// long operation
System.out.println("Hello");
}
}
}
Is there any standard way in Spring Framework 5.3.7+ to manage custom background routine in this manner?
Use #Scheduled:
#EventListener(ApplicationReadyEvent.class)
#Scheduled(fixedDelay = 5)
public void execute() {
(The #EventListener waits until the application is ready.)
You'll need to configure the thread pool in your application.properties:
spring.task.scheduling.pool.size: 4
You can also inject the ThreadPoolTaskExecutor to manage your other background tasks.
#Autowired private ThreadPoolTaskExecutor executor;
Is this the correct way to use #Async in Spring Boot?
#Service
class someServiceImpl {
...
public someResponseDTO getUsers(int userId) {
// Do some logic
...
// Call external API with another service method from another service impl
anotherService.emailUserInTheBackground(userId);
return someResponseDTO;
}
...
}
#Service
public class AnotherService {
#Async
public void emailUserInTheBackground(int userId) {
// This might take a while...
...
}
}
Since emailUserInTheBackground() has #Async annotation and void return type, does it block the line return someResponseDTO at all?
All I wanted is to return the response to the caller without waiting because emailUserInTheBackground() takes too long to complete and isn't directly tied to the response object.
Yes that is the correct way to run a task in the background, you can mimick the thread blocking behavior by introducing a delay.
#SpringBootApplication
#EnableAsync
public class MyApplication {
public static void main(String[] arg) {
SpringApplication.run(MyApplication.class);
}
}
then you need to mark the emailUserInTheBackground method with #Async annotation.
#Service
class AnotherService {
#Async
public void emailUserInTheBackground(int userId) {
try {
TimeUnit.SECONDS.sleep(10);
System.out.println("Print from async: "+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now add one more logger after a method call, you'll see getUsers(...) call completing first in a different thread even though the emailService thread is blocked for 10 seconds.
anotherService.emailUserInTheBackground(userId);
System.out.println("Print from service: "+ Thread.currentThread().getName());
you can also use CompletableFuture to run a task in the background.
public someResponseDTO getUsers(int userId) {
// some other task
...
// Call external API with another service method from another service impl
CompletableFuture.runAsync(() -> anotherService.emailUserInTheBackground(userId))
return someResponseDTO;
}
The relevant behavior of #Async is documented in the Spring documentation:
You can provide the #Async annotation on a method so that invocation of that method occurs asynchronously. In other words, the caller returns immediately upon invocation, while the actual execution of the method occurs in a task that has been submitted to a Spring TaskExecutor.
In the case you're describing, since the emailUserInTheBackground method is annotated with #Async and Spring's asynchronous method execution capability is enabled, the emailUserInTheBackground method will return immediately, and the call will be processed in a separate thread. The someResponseDTO value will be be returned from the getUsers method while the emailUserInTheBackground method continues to be processed in the background.
I am new to Async in Spring boot.
I have a bean A as follows:
class A {
private final B b;
private final C c;
...
int x = b.f();
c.g(x);
...
}
Here I would like to call both f() and g() in async. I have got some ideas from different articles regarding how to make #Async work. But, being a newbie, I cannot understand how would I call g() with the return value of f() in async.
It's pretty straight forward ,Add #EnableAsync annotation to a configuration class or to the main application class then add the #Async annotation to the method that you want to be executed asynchronously on a separate thread. Springboot will setup a thread pool and using proxy will automatically start a new thread to handle the method call. But if you want to return something from the method then use Future. You can also control which thread pool to use by creating a thread pool executor bean like below and specifying it in the #Async annotation.
#Configuration
#EnableAsync
class GeneralConfiguration {
#Bean(name = "asyncTaskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(20);
threadPoolTaskExecutor.setThreadNamePrefix("ScheduledTaskThread-");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
class B {
#Async("asyncTaskExecutor") //ask spring to use your thread pool for this async task.
public Future<Integer> f() {
//Do something
return new AsyncResult<Integer>(1);
}
}
class C {
#Async
public void g() {
//Do something
}
}
From your comments, in order to wait for thee result of method f of class B to be provided as input to the method g of class C then use CompletableFuture like :
class B {
#Async("asyncTaskExecutor") //ask spring to use your thread pool for this async task.
public CompletableFuture<Integer> f() {
//Do something
return CompletableFuture.completedFuture(1);
}
}
Then after while calling the method do something like :
...
CompletableFuture<Integer> result = b.f();
CompletableFuture.allOf(result).join(); //add other futures if you want to wait for other calls to complete.
c.g(result.get());
...
Obviously there are other optimized way to use Completable Future . But it depends on how you want to use this in your code. I suggest going through the completable future docs and finding out which suits your use case best.
PingInvoker.java
#Service
public class PingInvoker
{
#Scheduled(fixedRate = 5000) //<--how can I make this changeable while server is up and running
public void ping()
{
List<Server> svr = Manager.geList();
System.out.println("Invoking " + svr.size() + " Ping(s)");
for (Server i : svr)
i.ping();
}
}
The scheduler annotations are just a lightweight convenience for the simplest use cases. If you need access to more flexibility, such as runtime re-scheduling of tasks, then you need to use a different technique.
In your case, it should be sufficient to #Autowire a TaskScheduler field in your class, which you can then use to dynamically schedule your tasks. See the Spring docs for more info.