Spring Integration multiple queue consumers - spring-integration

As a result of Spring integration MessageQueue without polling, I have a poller that consumes messages from a queue instantly, using a custom TaskScheduler:
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultProcessor-");
IntegrationFlows
.from("inbound")
.channel(MessageChannels.priority().get())
.bridge(bridge -> bridge
.taskScheduler(taskScheduler)
.poller(Pollers.fixedDelay(0).receiveTimeout(Long.MAX_VALUE)))
.fixedSubscriberChannel()
.route(inboundRouter())
.get()
Now I'd like to have multiple threads consuming concurrently, so I tried:
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultProcessor-");
scheduler.setPoolSize(4);
However, since in AbstractPollingEndpoint the task scheduler schedules a synchronous poller (it's a bit complicated), only 1 thread is created. If I set the TaskExecutor to anything but SyncTaskExecutor (default), I run into a flood of scheduled tasks (see Spring integration MessageQueue without polling).
How can I concurrently consume from a queue in Spring Integration? This seems pretty basic but I couldn't find a solution.
Instead of the queue I could use an ExecutorChannel, however, (AFAIK) I then lose queue features like priority, queue size, and metrics on which I rely.

See PollerSpec.taskExecutor():
/**
* Specify an {#link Executor} to perform the {#code pollingTask}.
* #param taskExecutor the {#link Executor} to use.
* #return the spec.
*/
public PollerSpec taskExecutor(Executor taskExecutor) {
This way after scheduling the task periodically according your taskScheduler and delay, the real task is performed on a thread from that provided executor. By default it indeed performs the task on a scheduler's thread.
UPDATE
I'm not sure if this meets your requirements, but this is only way to keep your queue logic and process whatever is pull in parallel:
.bridge(bridge -> bridge
.taskScheduler(taskScheduler)
.poller(Pollers.fixedDelay(0).receiveTimeout(Long.MAX_VALUE)))
.channel(channels -> channel.executor(threadPoolExecutor()))
.fixedSubscriberChannel()

I was able to solve it like this:
A single-threaded task scheduler that performs the polling
A thread pool executor with a synchronous queue
This way, the task scheduler can give each executor 1 task and blocks when no executor is free, thus not draining the source queue or spamming tasks.
#Bean
public IntegrationFlow extractTaskResultFlow() {
return IntegrationFlows
.from(ChannelNames.TASK_RESULT_QUEUE)
.bridge(bridge -> bridge
.taskScheduler(taskResultTaskScheduler())
.poller(Pollers
.fixedDelay(0)
.taskExecutor(taskResultExecutor())
.receiveTimeout(Long.MAX_VALUE)))
.handle(resultProcessor)
.channel(ChannelNames.TASK_FINALIZER_CHANNEL)
.get();
}
#Bean
public TaskExecutor taskResultExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, // corePoolSize
8, // maximumPoolSize
1L, // keepAliveTime
TimeUnit.MINUTES,
new SynchronousQueue<>(),
new CustomizableThreadFactory("resultProcessor-")
);
executor.setRejectedExecutionHandler(new CallerBlocksPolicy(Long.MAX_VALUE));
return new ErrorHandlingTaskExecutor(executor, errorHandler);
}
#Bean
public TaskScheduler taskResultTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultPoller-");
return scheduler;
}
(The initial example was copied from the linked question, this one now resembles my actual solution)

Related

int-jdbc:inbound-channel-adapter channel depth

if the configured channel in int-jdbc:inbound-channel-adapter is full,after that polling happens or not ?
Can I use a queue channel in int-jdbc:inbound-channel-adapter?
My requirement is that if the channel is full,no DB call should make ,until channel is free.
You need to use a QueueChannel with restricted capacity. As far as any Inbound Channel Adapter uses a MessagingTemplate with the sendTimeout = -1, the QueueChannel performs a put() operation on the underlying LinkedBlockingQueue:
/**
* Inserts the specified element at the tail of this queue, waiting if
* necessary for space to become available.
*
* #throws InterruptedException {#inheritDoc}
* #throws NullPointerException {#inheritDoc}
*/
public void put(E e) throws InterruptedException {
To ensure that the poller is blocked until space is available you should use a fixed-delay option do not allow any parallel calls to DB.
So, in other words so far so good as long as you use default options.

How to pass on a traceId from gRPC's context to another thread/threadPool?

I am using grpc-java and have 3 services, A, B and C. I call service A and then service A calls B and C. I am using Hystrix in the calls to B and C. C in turn spawns another thread to call another service.
I have ClientInterceptors and ServerInterceptors which passes around the traceId. I can see the traceIds in the Context and logs as long as it is a gRPC worker thread but lose them when the call moves to another thread - RxIoScheduler thread or Hystrix thread. How do I pass the traceId around between requests on different threads and between different executor service and thread pools?
While it is possible to propagate in a fine-grained way (like executor.execute(Context.current().wrap(runnable))), you should try to integrate Context propagation into cross-thread work transfer. For many applications, that'd be as simple as wrapping the "main" executor as soon as it is created:
executor = Context.currentContextExecutor(executor);
// executor now auto-propagates
Do that once at the beginning of your application and then you mostly stop worrying about propagation.
But applications will vary. For example, applications that create Threads directly should probably make a ThreadFactory that propagates the calling thread's Context to the Thread:
class PropagatingThreadFactory implements ThreadFactory {
private final ThreadFactory delegate;
public PropagatingThreadFactory(ThreadFactory d) {delegate = d;}
#Override public Thread newThread(Runnable r) {
return delegate.newThread(Context.current().wrap(r));
}
}

junit spring boot ScheduledExecutorService schedule does not run

ScheduledExecutorService is used in a bean constructor, which should run when spring boot starts.
#RestController
public class TestController {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
public TestController() {
service.schedule(() -> {
// task
}, 0, TimeUnit.SECONDS);
service.shutdown();
}
However, sometimes, the task is never called. And in debug mode, service.shutdown() gives service as:
active threads = 1, queued tasks = 0, completed tasks = 0
While this does not happen all the time. When it runs as I think it should, service object at the same line gives either queued tasks or completed tasks 1.
This only happens during junit test,
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest()
#AutoConfigureMockMvc
public class ScheduledExecutorServiceTest {
#Test
public void test() {
}
}
You may delay the controller thread to wait for the execution of the scheduled thread with a call to
scheduledFuture.get() or
scheduledFuture.get(long timeout, TimeUnit unit),
where
scheduledFuture = service.schedule(..)
before
service.shutdown().
You are scheduling the task to run in another thread but shutting down the scheduler service immediately. shutdown method initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted and it does not wait for previously submitted tasks to complete execution. It may happen that sometimes your scheduler service is already shut down before getting the task, hence it shows queued or completed task as 0.

Java EE scheduled task and forced task

I'm working on a Java EE Web application.
I've developed a Scheduled Batch using #WebListener annotation:
#WebListener
public class BatchServlet implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
context = sce.getServletContext();
schedulerEngine = Executors.newSingleThreadScheduledExecutor();
schedulerEngine.scheduleAtFixedRate(new MyThread(), 0, 300, TimeUnit.SECONDS);
}
public class MyThread implements Runnable {
#Override
public void run() {
...... //my business logic
}
and it works correctly.
But, for my purpose, I need to force the MyThread() batch on demand, for example, when I received a particular request within a Servlet. Obviosly I can call the MyBatch thread separately, but I was wondering that there are some best practices to perform this operation.
There are a couple of options: create a regular thread pool (with multiple threads) and just put tasks on the executor queue whenever they come in.
If you need more fine-grained control, you can share something like a concurrent queue between the servlet and the one thread that you have. Let this thread read from the queue, it will block automatically when the queue is empty.
Then your servlet can put work on this queue, if the thread happened to be blocked on getting an item it will automatically wake up.
You might wanna look up the "producer-consumer" pattern. There is a lot of literature about this.

Spring #Async limit number of threads

My question is very similar to this one : #Async prevent a thread to continue until other thread have finished
Basically i need run ~ hundreds of computations in more threads. I want to run only some amount of parallel threads e.g. 5 threads with 5 computationis in paralell.
I am using spring framework and #Async option is natural choice. I do not need full-featured JMS queue, that`s a bit overhead for me.
Any ideas ?
Thank you
If you are using Spring's Java-configuration, your config class needs to implements AsyncConfigurer:
#Configuration
#EnableAsync
public class AppConfig implements AsyncConfigurer {
[...]
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
}
See #EnableAsync documentation for more details : http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html
Have you checked out Task Executor? You can define a Thread Pool, with a maximum number of threads to execute your tasks.
If you want to use it with #Async, use this in your spring-config:
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
Full reference here (25.5.3). Hope this helps.
Since spring boot 2.1 you can use auto configuration and change the maximum number of threads in the application properties file
spring.task.execution.pool.max-size=4
See the full documentation:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-task-execution-scheduling

Resources