How to specify a thread pool for a specific process - java

I've configuration that is implementing AsyncConfigurer as bellow:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(5);
executor.setCorePoolSize(5);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
That new thread pool is working fine with that:
#Component
public class MessageJmsListener {
#Async
#EventListener
public void onApplicationEvent(CustomMessage messageJmsWrapper) {
log.info("Current thread: {}", Thread.currentThread().getName());
}
#Async
#EventListener
public void onApplicationEventSecond(SecondCustomMessage messageJmsWrapper) {
log.info("Second Listener thread: {} , Thread.currentThread().getName());
}
}
And I would like to achieve such an effect that for the first listener I give a separate bean (number of threads) and for the second. Is such a thing possible?
Thanks in advance!
Best Regards

It looks like that using qualifiers could help you.
Create two different executors and add different qualifiers for each.
Use the first one for the first listener with one one parameter set and the second one for the other one with different parameter set. See example in this thread.
Another approach in your case could be that you call the right executor by adding executor name in annotation where you are invoking it.
Example:
#Configuration
#EnableAsync
class ApplicationAsyncConfiguration {
#Bean(name = "maxThreadingExecutor")
public Executor maxThreadingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(5);
executor.setCorePoolSize(5);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
#Bean(name = "defaultExecutor")
public Executor defaultExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(2);
executor.setCorePoolSize(2);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
}
#Component
public class MessageJmsListener {
#Async("maxThreadingExecutor")
#EventListener
public void onApplicationEvent(CustomMessage messageJmsWrapper) {
log.info("Current thread: {}", Thread.currentThread().getName());
}
#Async("defaultExecutor")
#EventListener
public void onApplicationEventSecond(SecondCustomMessage messageJmsWrapper) {
log.info("Second Listener thread: {}", Thread.currentThread().getName());
}
}
See more in this answer, might be helpful.

Related

How to delay application shutdown while running #Async tasks?

I have an async task executor.
How can I tell Spring to wait on application shutdown until all tasks are finished?
#Bean
public ThreadPoolExecutor singleExecutor() {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(10),
new ThreadPoolExecutor.DiscardPolicy());
}
#Service
class MyService {
#Async("singleExecutor")
public void runAsync() {
}
}
Solution
The org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor class (implements the org.springframework.core.task.TaskExecutor interface) supports the requested functionality by the the following methods:
setWaitForTasksToCompleteOnShutdown().
setAwaitTerminationSeconds().
Therefore, please, consider migrating the bean:
From: java.util.concurrent.ThreadPoolExecutor.
To: org.springframework.core.task.TaskExecutor.
«Migrated bean» example
#Bean
public TaskExecutor singleExecutor() {
final ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setKeepAliveSeconds(0);
taskExecutor.setQueueCapacity(10);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(300);
return taskExecutor;
}
ThreadPoolTaskExecutor class: Queue type
Please, see the Javadoc for the ThreadPoolTaskExecutor.createQueue() method on the default queue factory method behaviour:
protected BlockingQueue<Runnable> createQueue(int queueCapacity)
Create the BlockingQueue to use for the ThreadPoolExecutor.
A LinkedBlockingQueue instance will be created for a positive capacity value; a SynchronousQueue else.
It is possible to override the behaviour by using a class derived from the ThreadPoolTaskExecutor class with the overridden factory method.
For example:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
public final class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
#Override
protected BlockingQueue<Runnable> createQueue(final int queueCapacity) {
return new ArrayBlockingQueue<>(queueCapacity);
}
}

How to disable EnableAsync for one of the executor to wait all task finish?

I'm using #EnableAsync and #Async for enabling asynchronous call when submitting request to thread pool. This work well, as it doesn't block the flow at future.get(). Now, we added another bean processExecutor within MySpringBootApplication class. But for this we wanted to wait till it completes all the task when submitted to this executor. But actually, it continues and doesn't wait at future.get(). Can someone help me, what can I make change so that it waits for all thread to complete ?
#SpringBootApplication
#EnableAsync
public class MySpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
#Bean(name="calculateExecutor")
public TaskExecutor calExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(MySpringBootApplication.class,args);
}
}
Following is Service class
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public Future<String> process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
//API call which takes 2-3 seconds to complete
return new AsyncResult<String>(resp.getBody().getDataId())
}
}
I'm calling future.get() from other component class.

Spring #Async propagate context information

I've a Spring Boot 2.2 application. I created a service like this:
#Async
#PreAuthorize("hasAnyRole('ROLE_PBX')")
#PlanAuthorization(allowedPlans = {PlanType.BUSINESS, PlanType.ENTERPRISE})
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
log.debug("Current tenant {}", TenantContext.getCurrentTenantId());
return new AsyncResult<AuditCdr>(auditCdrRepository.save(cdr3CXMapper.cdr3CXDtoToAuditCdr(cdrRecord)));
}
this is my #Async configuration:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
Using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL I see the Security context is passed to the #Async method.
In my multi-tenant application I use a ThreadLocal to set the tenant's id:
public class TenantContext {
public final static String TENANT_DEFAULT = "empty";
private static final ThreadLocal<String> code = new ThreadLocal<>();
public static void setCurrentTenantId(String code) {
if (code != null)
TenantContext.code.set(code);
}
public static String getCurrentTenantId() {
String tenantId = code.get();
if (StringUtils.isNotBlank(tenantId)) {
return tenantId;
}
return TENANT_DEFAULT;
}
public static void clear() {
code.remove();
}
}
Because ThreadLocal is related to the thread, it's not available in the #Async method. Furthemore my custom #PlanAuthorization aop needs it to perform verifications of the tenant's plan.
Is there a clean way to set TenantContext in any #Async method in my application?
I ended up to use a TaskDecorator:
#Log4j2
public class MdcTaskDecorator implements TaskDecorator {
#Override
public Runnable decorate(Runnable runnable) {
// Right now: Web thread context !
// (Grab the current thread MDC data)
String tenantId = TenantContext.getCurrentTenantId();
Long storeId = StoreContext.getCurrentStoreId();
SecurityContext securityContext = SecurityContextHolder.getContext();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
log.info("Saving tenant information for async thread...");
return () -> {
try {
// Right now: #Async thread context !
// (Restore the Web thread context's MDC data)
TenantContext.setCurrentTenantId(tenantId);
StoreContext.setCurrentStoreId(storeId);
SecurityContextHolder.setContext(securityContext);
MDC.setContextMap(contextMap);
log.info("Restoring tenant information for async thread...");
runnable.run();
} catch (Throwable e) {
log.error("Error in async task", e);
} finally {
MDC.clear();
}
};
}
}
and I used it in this way:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
It works and it seems also a neat solution.
The solution for such case is to :
configure custom thread pool so that you override it's execute method to sets up your thread local (or
executes any task from your main context), decorate the task and submit decorated task for execution instead of original one
instruct #Async annotation to use concrete thread pool
#Bean("tenantExecutor")
public Executor threadLocalAwareThreadPool() {
final CustomizableThreadFactory threadNameAwareFactory =
new CustomizableThreadFactory("threadAsync");
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(500), threadNameAwareFactory) {
// override original method of thread pool
#Override
public void execute(Runnable originalTask) {
final String tenantId = tenantThreadLocal.get(); // read data from current before passing the task to async thread
// decorate the actual task by creating new task (Runnable) where you first set up the thread local and then execute your actual task
super.execute(() -> {
tenantThreadLocal.set(tenantId); // set data in actual async thread
originalTask.run();
});
}
};
return threadPoolExecutor;
}
Now we tell spring use our custom executor
#Async("tenantExecutor")
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
// your code....
}
Instead of ThreadLocal you must use InheritableThreadLocal. Then you will see the values from the parent thread.
API Doc: https://docs.oracle.com/javase/8/docs/api/java/lang/InheritableThreadLocal.html
Here is an article about this in combination with Spring: https://medium.com/#hariohmprasath/async-process-using-spring-and-injecting-user-context-6f1af16e9759

Shutdown #Bean ExecutorService using #PreDestroy

I have a Spring #Configuration class as follows:
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public BlockingQueue<Runnable> queue() {
return ArrayBlockingQueue<>(1000);
}
#Bean
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue);
}
#PreDestroy
public void shutdownExecutor() {
// no executor instance
}
}
I would also like to specify a #PreDestroy method which shuts down the ExecutorService. However, the #PreDestroy method cannot have any arguments which is why I'm not able to pass the executor bean to this method in order to shut it. Specifying destroy method in #Bean(destroyMethod = "...") does not work either. It allows me to specify existing shutdown or shutdownNow, but not the custom method which I intend to use.
I know I could instantiate the queue and executor directly and not as Spring beans, but I'd rather do it this way.
I love defining classes inline:
#Bean(destroyMethod = "myCustomShutdown")
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue) {
public void myCustomShutdown() {
...
}
};
}
Use the ThreadPoolTaskExecutor which does all that by default.
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() {
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
return new ArrayBlockingQueue<>(queueCapacity);
}
};
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setKeepAliveSeconds(0);
taskExecutor.setQueueCapacity(1000);
return taskExecutor;
}
}
This will configure the ThreadPoolExecutor and shutdown when the application stops.
If you don't need the ArrayBlockingQueue but can live with the default LinkedBlockingQueue and only need to specify the queue capacity you can remove the override createQueue method.

Custom ThreadPoolTaskExecutor with Spring Boot Async

I have a Spring Boot application which is responsible for answering requests via REST. I also push metrics about my application call. Since this is a separate task and I have to response users immediately, I want to make that metrics publishing asynchronously. So I've used that:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("MyApp-");
executor.initialize();
return executor;
However, this one uses SimpleAsyncTaskExecutor and does not reuse any threads.
1) How can I use ConcurrentTaskExecutor instead of SimpleAsyncTaskExecutor?
2) Which implementation could best fit for my needs?
To make your custom Executor work, make sure it's registered as a Bean, and reference it on the method annotated with #Async:
#Bean(name = "transcodingPoolTaskExecutor")
public Executor transcodingPoolTaskExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("transcoder-");
executor.initialize();
return executor;
}
And in the #Service which is holding the method:
#Async("transcodingPoolTaskExecutor")
public void transcodeVideo(UUID fileId) {
mediaFileService.transcodeVideo(fileId);
}
You can also redefine the default executor by creating a Configuration class which implements AsyncConfigurer. Something like this:
#EnableAsync
#Configuration
public class AsyncConfig implements AsyncConfigurer {
#Value("${async.executor.corePoolSize:20}")
private int corePoolSize;
#Value("${async.executor.maxPoolSize:100}")
private int maxPoolSize;
#Value("${async.executor.queueCapacity:50}")
private int queueCapacity;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("Async Executor -");
executor.initialize();
return executor;
}
}
In this case you wouldn't need to specify the executor name in #Async annotation. Spring will take executor, which you defined in AsyncConfig.
It's very similar to the solution provided by yglodt, but in this case you don't need to specify the name of executor in every #Async annotation, so it removes potential misspelling in the executor name.

Categories

Resources