How to add Asynchronous execution in spring boot application? - java

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();
}
}

Related

Spring TaskScheduler does not schedule task immediatelly

I want to execute the same task couple of times but it seems that every next invocation of my code does not execute the task immediately, for example it executes after one minute.
Since user has to schedule tasks manually I use ScheduledTaskRegistrar.TaskScheduler.
taskRegistrar.getScheduler().schedule(myTask, new Date());
What could be the reason? User clicked schedule button twice on my fronted application and backend invoked the above schedule method twice as expected. First execution of my task was immediate, second run after two minutes.
UPDATE: taskregistrar config, maybe I didn't configure it at all. my tasks are added as cron tasks on application deployment. But they also must be runnable manually if user wants to trigger it. Below is more or less the whole logic:
#Configuration
#EnableAsync
#EnableScheduling
#Component
#Slf4j
#Generated
#Getter
public class ScheduleTaskService implements SchedulingConfigurer {
#Autowired
private List< MyTask> taskList;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
taskList.stream().filter(MyTask::isOn).forEach(this::addTaskToScheduler);
}
public void addTaskToScheduler(GwoTask task) {
taskRegistrar.addCronTask(task, task.getCronExpression());
}
public void scheduleImmediateInvocation(MyTask myTask) {
taskRegistrar.getScheduler().schedule(myTask, new Date());
}
}
By referring to the source code of ScheduledTaskRegistrar,
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
...
If we do not set taskScheduler, Executors.newSingleThreadScheduledExecutor() is used by default. Hence new task will be blocked by processing task.
For your use case in scheduleImmediateInvocation, I recommend to use another thread pool(Probably from Executors) instead as:
It isn't actually a schedule job.
More control on pool size is needed to suit your workload
If you just want to make ScheduledTaskRegistrar execute more concurrently, configure it as:
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// set the desired core pool size
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
this.taskRegistrar = taskRegistrar;
// ...
}

Why Spring use ForkPoolJoin instead of ThreadPoolTaskExecutor with #Async?

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();
}
}

Whatis the standard way to manage (check and recover) a custom backgtround routine in Spring?

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;

Can I return an API response without waiting on other external API calls in Spring Boot?

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.

Multiple threads submit tasks and wait for results while another thread periodically executes each task

I have multiple threads that consume some data and call one third party service (serviceA). I can send only one request to serviceA per 10 second. Each thread have to wait until it receives a result from serviceA and then continue doing other thread specific work.
I want to implement some sort of proxy for serviceA that will receive all calls for serviceA, collect them, execute one call per 10 seconds and return a result of this call. Each thread should wait until the proxy returns the result. It should look something like this
public class ServiceAProxy implements ServiceA {
private ServiceA serviceA;
private ??? callsHolder;
public ServiceAProxy(ServiceA serviceA) {
this.serviceA = serviceA;
}
public Result call(String parameter) {
return callsHolder.submitAndWaitResult(() -> serviceA.call(parameter));
}
#Scheduled(fixedDelay = 10000)
public void executeOldestCall() {
callsHolder.executeOldestTask();
}
}
Probably callHolder could be implemented using 2 SynchronousQueues but is there any cleaner solution to do this without reinventing the wheel?
In case number of threads is small and blocking of a calling thread until it can send the request is not a big deal, Guava RateLimiter may be just enough. So your service proxy would look something like this:
public class ServiceAProxy implements ServiceA {
private final ServiceA serviceA;
private final RateLimiter throttle;
public ServiceAProxy(ServiceA serviceA, double callsPerSecond) {
this.serviceA = serviceA;
throttle = RateLimiter.create(callsPerSecond);
}
public Result call(String parameter) {
// every thread may potentially block here until throttle allows it to proceed
throttle.acquire();
return serviceA.call(parameter);
}
}

Categories

Resources