Mixing Java `ExecutorService` with Spring `TaskExecutor` - java

I have a piece of Java code that I previously used without Spring that looks like this:
// `Callable` instead of `Runnable` because we need to throw exceptions
public MyTask extends Callable<Void> {
#Override
public Void call() throws Exception { ... }
}
public class MyTasksRunner {
private final ExecutorService executorService;
...
public void run() throws Exception {
List<MyTask> tasks = ...;
var futures = executorService.invokeAll(tasks);
for (var future : futures) {
// Rethrow any exceptions happened in the threads.
future.get();
}
}
}
Now I'm merging this code into a larger Spring Boot application that has async enabled. It configures a TaskExecutor, which doesn't have the same interface as ExecutorService. A TaskExecutor can only run Runnables, not Callables.
I can probably have a TaskExecutor bean for async Spring, and another ExecutorService bean for the MyTasksRunner code at the same time. But I wonder what options I have if I want to merge those:
Can I tell Spring to use an ExecutorService for its async stuff?
Can I convert my Callable code to use Runnables instead, while still being able to propagate exceptions from the tasks?
I also thought about just making MyTask a Spring component and annotating it with #Async, but I don't really like that it makes the MyTask* code tied to Spring.

Yes, you can convert your Callable task to Runnable as I see you don't expect any return value. But with one condition - you cant throw Checked Exception however you may continue throwing Runtime Exception.
Also, yes you can define Executor bean as below to inject ExecutorService
#Bean
public Executor taskExecutor() {
ExecutorService executor = Executors.newFixedThreadPool(2);
return executor;
}
If you dont define an Executor bean, Spring creates SimpleAsyncTaskExecutor and uses that.

Related

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;

How to call multiple functions of different Spring beans in async

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.

Java Spring boot async method isn't being called occasionally

I have a service with a method annotated with #Async as below:
#Service("AsyncService")
public class AsyncService {
#Async
public void asyncPrint() {
logger.info("Inside asyncPrint");
}
}
The async method is called from another service like this:
#Service("CallerService")
public class CallerService {
#Autowired
private AsyncService asyncService;
public void caller() {
logger.info("Before asyncPrint");
asyncService.asyncPrint();
logger.info("After asyncPrint");
// Custom code
}
}
However, I'm seeing that sometimes the async method is not being called. I can see the before and after logs, but the async log is not printed.
Sample o/p:
//Logs from other services
Before asyncPrint
After asyncPrint
Inside asyncPrint
//Logs from other services
Before asyncPrint
After asyncPrint
Why could this be happening? Can async tasks be dropped if there is a backlog in the Executor? Also, is there a way for me to see how many of the total running threads are being used by the async? Or how else can I debug this?
Thanks.
The log doesn't mean your Async method isn't being called, it means the Executor hasn't called it yet. The #Async uses a ThreadPoolTaskExecutor by default with unlimited queue capacity. This means ALL your tasks will be queued until processing threads become available.
If you want to increase your processing Threads you can defined a custom ThreadPoolTaskExecutor bean as below.
#Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
Where you can play with corePoolSize and maxPoolSize according to your hardware specifications.

How to get the queue size of the executor in real time

Supposed i have this application.java
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(50);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(5000);
executor.setThreadNamePrefix("sm-async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
My goal is to create an alert if the current real time queue size of the async executor is in 80% or nearly the limit. I think we can get the value from executor.getThreadPoolExecutor().getQueue().size();. Im currently stuck on how to achieve that
#Controller
public class QueueMonitorController {
#Autowired
private Executor executor;
#RequestMapping(value = "/queuesize", method = RequestMethod.GET)
public int queueSize() {
ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
return tpe.getQueue().size();
}
}
If you can provide the bean as a ThreadPoolExecutor, then you don't even need the cast. The internal implementation of size() in LinkedBlockingQueue (which ThreadPoolExecutor uses) is AtomicInteger.get().
So there's no need to get creative and build your own mechanisms, it's all built-in. Based on Spring 4.2, but shouldn't depend on the version too much.
So the root goal is to monitor the queue, and send an alert when queue is 80% full. This should not go into your code which is responsible for making sure that your business logic works. You shouldn't make hacks there to account for lack of resources. If the idea is that you should throttle users when the queue is packed, there are far better ways to handle those.
Since the idea is to do "light monitoring", i.e. there's no attempt to handle a case when queue is 80% full, a polling solution would be lightweight enough. Considering that the executor can be easily injected to a separate Controller, it won't even mess up your "real" code.
As ThreadPoolTaskExecutor does not expose any API you can get the queue used by it. However, you are free to extend ThreadPoolTaskExecutor and create a CustomThreadPoolTaskExecutor override the createQueue.
public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor{
private BlockingQueue<Runnable> queue;
#Override
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
queue = super.createQueue(queueCapacity);
return queue;
}
public BlockingQueue<Runnable> getQueue(){
return queue;
}
}
Now you can create asyncExecutor like below :
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
//set other properties
executor.initialize();
return executor;
}
Your CustomThreadPoolTaskExecutor executor has public method getQueue and you can use that to get the queue.
I don't know from where you have got ThreadPoolTaskExecutor class type of executor. But in java you can typecast to ThreadPoolExecutor and get queue and it's size as below:
ThreadPoolExecutor executorService = (ThreadPoolExecutor )Executors.newCachedThreadPool();
executorService.getQueue().size()
To do this in real-time as you're asking for is not so easy. You'll need to decorate the methods of BlockingQueue so that you can add code to execute immediately when the content of the queue changes.
You can then provide your queue to Spring's ThreadPoolTaskExecutor like this:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {
#Override
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
// create and return your instance of blocking queue here
}
};

Categories

Resources