multi subscriber of a spring integration ExecutorChannel - java

In my application I configure some channels as follows:
#Bean
public MessageChannel eventFilterChannel() {
return new ExecutorChannel(asyncConfiguration.getAsyncExecutor());
}
#Bean
public MessageChannel processEventChannel() {
return new ExecutorChannel(asyncConfiguration.getAsyncExecutor());
}
I am using ExecutorChannel and using my custom Executor as follows:
#Configuration
#EnableAsync
public class AsyncConfiguration extends AsyncConfigurerSupport {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("MyAppThread");
executor.initialize();
return executor;
}
}
I have the following MessageEndpoint which is a subscriber to the eventFilterChannel channel:
#MessageEndpoint
public class MyEventFilter {
#Filter(inputChannel = "eventFilterChannel", outputChannel = "processEventChannel")
public boolean filterEvents(final MyEvent myEvent) {
//filter logic
}
}
Ideally, I would expect my event filter message endpoint to be multi-threaded as I am using ExecutorChannel. I would like to understand if this is the correct implementation of multithreaded endpoint?
However, I am doubtful because I could see the following in my logs:
Channel 'application.eventFilterChannel' has 1 subscriber(s).
Is my implementation correct or is there a standard I can follow?

Well, there is a bit of misleading. Your eventFilterChannel really has only one subscriber - your #Filter. But it is indeed multi-threaded. The same stateless component is used in several threads.
The ExecutorChannel queues incoming tasks and they are performed on the threads in the pool - in parallel. In our case the story is about messages delivery. Not sure if code can help you but it looks like:
public final boolean dispatch(final Message<?> message) {
if (this.executor != null) {
Runnable task = createMessageHandlingTask(message);
this.executor.execute(task);
return true;
}
return this.doDispatch(message);
}
Where that Runnable is like this:
public void run() {
doDispatch(message);
}
...
handler.handleMessage(message);
This handler is exactly a subscriber for that #Filter.
So, the same method is called from different threads. Since this is passive and stateless component it is just safe to keep it only once and reuse from different threads.
On the other hand, out of topic: if you add more subscribers to this channel, they are not going to be called in parallel anyway: By default it is round-robin strategy: the handler for next message is selected according the index.
If one handler fails to process message, we try the next and so on. You can inject any other custom implementation though. Or even reset it to null to always start from the first one.

Related

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 use blocking queue in Spring Boot?

I am trying to use BlockingQueue inside Spring Boot. My design was like this: user submit request via a controller and controller in turn puts some objects onto a blocking queue. After that the consumer should be able to take the objects and process further.
I have used Asnyc, ThreadPool and EventListener. However with my code below I found consumer class is not consuming objects. Could you please help point out how to improve?
Queue Configuration
#Bean
public BlockingQueue<MyObject> myQueue() {
return new PriorityBlockingQueue<>();
}
#Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("Test-");
executor.initialize();
return executor;
}
Rest Controller
#Autowired
BlockingQueue<MyObject> myQueue;
#RequestMapping(path = "/api/produce")
public void produce() {
/* Do something */
MyObject myObject = new MyObject();
myQueue.put(myObject);
}
Consumer Class
#Autowired
private BlockingQueue<MyObject> myQueue;
#EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
consume();
}
#Async
public void consume() {
while (true) {
try {
MyObject myObject = myQueue.take();
}
catch (Exception e) {
}
}
}
Your idea is using Queue to store messages, consumer listens to spring events and consume.
I didn't see your code have actually publish the event, just store them in queue.
If you want to use Spring Events, producers could like this:
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void doStuffAndPublishAnEvent(final String message) {
System.out.println("Publishing custom event. ");
CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
applicationEventPublisher.publishEvent(customSpringEvent);
}
check this doc
If you still want to use BlockingQueue, your consumer should be a running thread, continuously waiting for tasks in the queue, like:
public class NumbersConsumer implements Runnable {
private BlockingQueue<Integer> queue;
private final int poisonPill;
public NumbersConsumer(BlockingQueue<Integer> queue, int poisonPill) {
this.queue = queue;
this.poisonPill = poisonPill;
}
public void run() {
try {
while (true) {
Integer number = queue.take(); // always waiting
if (number.equals(poisonPill)) {
return;
}
System.out.println(Thread.currentThread().getName() + " result: " + number);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
could check this code example
#Async doesn't actually start a new thread if the target method is called from within the same object instance, this could be the problem in your case.
Also note that you need to put #EnableAsync on a config class to enable the #Async annotation.
See Spring documentation: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#scheduling-annotation-support
The default advice mode for processing #Async annotations is proxy which allows for interception of calls through the proxy only. Local calls within the same class cannot get intercepted that way. For a more advanced mode of interception, consider switching to aspectj mode in combination with compile-time or load-time weaving.
In the end I came up with this solution.
Rest Controller
#Autowired
BlockingQueue<MyObject> myQueue;
#RequestMapping(path = "/api/produce")
public void produce() {
/* Do something */
MyObject myObject = new MyObject();
myQueue.put(myObject);
Consumer.consume();
}
It is a little bit weird because you have to first put the object on queue yourself then consume that object by yourself. Any suggestions on improvement is highly appreciated.

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

ThreadPoolTaskExecutor spawning unwanted threads

I have two Async methods that are configured to use the same TaskExecutor.
#Async("myExecutor")
public void foo() {
system.out.println("foo from: " + Thread.currentThread().getName());
}
#Async // this should use the primary defaultExecutor
// try to bombard the single thread pool with bunch of requests
public void generateFoo() {
while(true) {
system.out.println("generateBar from: " + Thread.currentThread().getName());
this.foo();
}
}
I want the underlining threadpool of myExecutor to have a coresize and maxsize of 1 and queue incoming requests for the thread. I.e only one thread executing foo() runs at a time and all other calls to foo() waits for their turn. In my Spring configuration class, I have
#Configuration
#ComponentScan(basePackages = { "com.test" })
#EnableAsync
#EnableScheduling
public class FooBarConfig {
#Bean
#Primary
#Qualifier("defaultExecutor")
public TaskExecutor defaultExecutor() {
return new ThreadPoolTaskExecutor();
}
#Bean
#Qualifier("myExecutor")
public TaskExecutor myExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(Integer.MAX_VALUE);
return executor;
}
}
However, I'm seeing that foo() isn't always being executed from the same thread. If I call foo() via a #Scheduled task from another thread, it runs on "myExecutor-1" as expected. But if foo() is called via generateFoo(), it seems to be running in whatever generateFoo() runs from, ie:
foo from: defaultExecutor-1
foo from: myExecutor-1
foo from: defaultExecutor-1
foo from: defaultExecutor-1
foo from: defaultExecutor-1
If I get rid of generateFoo()'s #Async, foo() still runs on whatever thread generateFoo() is running on.
Am I using the wrong TaskExecutor for what I'm looking for or configuring it incorrectly?
edit
As state in my original post, not making generateFoo() an async method doesn't seem to do the trick, as someone suggested.

Categories

Resources