1

I have a dsl flow which works great with Queue channel. However, when i make it synchronous using Rendezvous channel, i get acknowledgement rate at most 30 messages/second. My handlers are take just 350 microseconds to finish the process but the acknowledgement rate keeps low. This piles up rabbit queue drastically. I even scaled concurrent consumers to 10 and increased prefetch too but this did not help. Then i added couple of more scaled instances itself but that helped raise ack rate to around 45/sec.

How can I make flow acknowledge faster? I am expecting rate of over 500 per second.

Dsl Flow:

SimpleMessageListenerContainer simpleMessageListenerContainer = profileTagRabbitMLCConfig.transactedChannelSpanRabbitSMLC(queueName)

simpleMessageListenerContainer?.setConcurrentConsumers(concurrentConsumer)
            simpleMessageListenerContainer?.setPrefetchCount(prefetch)

            return IntegrationFlows.from(Amqp.inboundAdapter(simpleMessageListenerContainer))
                    .channel(rendezvousTransformerChannel1())
                    .transform(myTransformer, 'transform', { e -> e.advice(adviceWithRecoverer) })
                    .channel(rendezvousTransformerChannel2())
                    .handle(myHandler, 'save', { e -> e.advice(adviceWithRecoverer) })
                    .get()

Synchronous Channels:

@Bean
MessageChannel rendezvousTransformerChannel1() {
    return MessageChannels.rendezvous().get()
}

@Bean
MessageChannel rendezvousHandlerChannel() {
    return MessageChannels.rendezvous().get()
}

Container:

SimpleMessageListenerContainer 
transactedChannelSpanRabbitSMLC(CachingConnectionFactory rabbitConnectionFactory, String queueName){

    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer()
    container.setConnectionFactory(rabbitConnectionFactory)
    container.setQueueNames(queueName)
    container.setChannelTransacted(true)
    container
}

Recovery advice for retry:

Advice getRetryAdviceWithRecovery() {
    RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice()
    advice.setRetryTemplate(getRetryTemplate())
    advice.recoveryCallback = getRecoveryCallback() // sends message to rabbit exchange
    advice
}

Poller:

@Bean(name = PollerMetadata.DEFAULT_POLLER)
    public PollerMetadata poller() {
        return Pollers.fixedDelay(100).maxMessagesPerPoll(500L).get();
    }
James Z
  • 12,209
  • 10
  • 24
  • 44

1 Answers1

0

What is your use case that drives you to use RendezvousChannels ?

It's quite rare, and I don't think I've ever seen 2 in the same flow.

You must have a poller for this channel type; I don't see them so it implies you have a default poller bean.

You need to show your poller, but I suspect it is not well tuned for this. A RendezvousChannel send() blocks until something does a receive().

Regardless, you risk message loss if you use any kind of thread handoff (QueueChannel, RendezvousChannel) on a listener container thread.

You should probably just remove those .channel()s from your flow which will use DirectChannels instead.

If you want concurrency, use the concurrentConsumers property on the listener container.

container.setChannelTransacted(true)

If you are publishing messages in myHandler, transactions are very expensive, too.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks Gary. Just moved code to original description. My use case is to wait on AUTO ack untill handler processes requests completely. When i use queuechannel, the message acknowledges immediately even when transformer/handler not finished processing message. For this reason i had even relied on InboundGateway. – Pradeepkumar Patil Mar 04 '18 at 02:38
  • You should not use `QueueChannel` or `RendezvousChannel` at all here; you want the handler to run on the container thread; remove the `.channel()` completely so the `AUTO` ack doesn't happen until the `myHandler.save()` returns. `.from().transform().handle()`. An inbound gateway is not appropriate here either. – Gary Russell Mar 04 '18 at 02:41
  • I will try the suggested approach. Thanks Gary on you quick help today. – Pradeepkumar Patil Mar 04 '18 at 02:45
  • Use Case+: The reason we wanted pollable channel because few of the REST endpoints which our handler hits down the line, can not handle great amount of speed. Therefore we configured default poller which would throttle every 100 miliseconds and poll just 500 messages each time. When we used queuechannel, we configured 1000 as queue depth. Rabbit queue has 1 million msgs. By moving away from pollable channels to subscribable like directchannel(default), can i still achieve message throttling as well as transactional AUTO acknowledgement until handler.save() returns concurrently? – Pradeepkumar Patil Mar 04 '18 at 05:01
  • It's the wrong architecture since the message is acknowledged as soon as the poller fetches the message from the first `RendezvousChannel`. If you want to control the message rate, consider using the new [Polled Inbound Channel Adapter](https://docs.spring.io/spring-integration/reference/html/amqp.html#_polled_inbound_channel_adapter) and the `MessageSourcePollingTemplate ` where you can control the message consumption rate. – Gary Russell Mar 04 '18 at 05:22