[英]Spring Cloud Stream - how to handle downstream blocks?
During a planned downtime for our Kafka cluster, we basically encountered the following issue How to specify timeout for sending message to RabbitMQ using Spring Cloud Stream?在我们的 Kafka 集群计划停机期间,我们基本上遇到了以下问题如何使用 Spring Cloud Stream 指定向 RabbitMQ 发送消息的超时时间? (with Kafka rather than RabbitMQ, obviously).
(显然,使用 Kafka 而不是 RabbitMQ)。
The answer from @GaryRussell: @GaryRussell 的回答:
The channel
sendTimeout
only applies if the channel itself can block, eg aQueueChannel
with a bounded queue that is currently full;通道
sendTimeout
仅适用于通道本身可以阻塞的情况,例如当前已满的有界队列的QueueChannel
; the caller will block until either space becomes available in the queue, or the timeout occurs.调用者将阻塞,直到队列中有可用空间,或者发生超时。
In this case, the block is downstream of the channel so the sendTimeout is irrelevant (in any case, it's a DirectChannel which can't block anyway, the subscribed handler is called directly on the calling thread).
在这种情况下,块位于通道的下游,因此 sendTimeout 是无关紧要的(无论如何,它是一个 DirectChannel,无论如何都不能阻塞,订阅的处理程序直接在调用线程上调用)。
The actual blocking you are seeing is most likely in the
socket.write()
in the rabbitmq client, which does not have a timeout and is not interruptible;您看到的实际阻塞很可能在 rabbitmq 客户端中的
socket.write()
中,它没有超时且不可中断; there is nothing that can be done by the calling thread to "time out" the write.调用线程无法执行任何操作来“超时”写入。
The only possible solution I am aware of is to force close the rabbit connection by calling
resetConnection()
on the connection factory.我知道的唯一可能的解决方案是通过在连接工厂上调用
resetConnection()
来强制关闭兔子连接。
explains quite well why the method in question ( org.springframework.integration.channel.AbstractSubscribableChannel#doSend
) does not take the timeout
into account.很好地解释了为什么有问题的方法(
org.springframework.integration.channel.AbstractSubscribableChannel#doSend
)没有考虑timeout
。 However, this still seems a bit odd to me.但是,这对我来说仍然有点奇怪。
In spring-integration-kafka-3.2.1.RELEASE-sources.jar./org/springframework/integration/kafka/outbound/KafkaProducerMessageHandler:java:566
, we can see that, if sync
behaviour is desired:在
spring-integration-kafka-3.2.1.RELEASE-sources.jar./org/springframework/integration/kafka/outbound/KafkaProducerMessageHandler:java:566
中,我们可以看到,如果需要sync
行为:
565 if (this.sync) {
566 Long sendTimeout = this.sendTimeoutExpression.getValue(this.evaluationContext, message, Long.class);
567 if (sendTimeout == null || sendTimeout < 0) {
568 future.get();
569 }
570 else {
571 try {
572 future.get(sendTimeout, TimeUnit.MILLISECONDS);
573 }
574 catch (TimeoutException te) {
575 throw new MessageTimeoutException(message, "Timeout waiting for response from KafkaProducer", te);
576 }
577 }
578 }
is called, where a timeout is taken into consideration.被调用,其中考虑了超时。 The
sendTimeoutExpression
is assigned to a default Value: sendTimeoutExpression
被分配给一个默认值:
private static final long DEFAULT_SEND_TIMEOUT = 10000;
private Expression sendTimeoutExpression = new ValueExpression<>(DEFAULT_SEND_TIMEOUT);
however, our stack trace reveals something different:然而,我们的堆栈跟踪揭示了一些不同的东西:
"pool-1-thread-3" - Thread t@108
java.lang.Thread.State: TIMED_WAITING
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <4ebda621> (a org.springframework.util.concurrent.SettableListenableFuture$SettableTask)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:426)
at java.util.concurrent.FutureTask.get(FutureTask.java:204)
at org.springframework.util.concurrent.SettableListenableFuture.get(SettableListenableFuture.java:134)
* at org.springframework.integration.kafka.outbound.KafkaProducerMessageHandler.processSendResult(KafkaProducerMessageHandler.java:572)
at org.springframework.integration.kafka.outbound.KafkaProducerMessageHandler.handleRequestMessage(KafkaProducerMessageHandler.java:414)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:69)
at org.springframework.cloud.stream.binder.AbstractMessageChannelBinder$SendingHandler.handleMessageInternal(AbstractMessageChannelBinder.java:1035)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:69)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:570)
The invocation marked with *
corresponds to the future.get(sendTimeout, TimeUnit.MILLISECONDS);
标有
*
的调用对应于future.get(sendTimeout, TimeUnit.MILLISECONDS);
call.称呼。
Seeing how the underlying client seems to support it (given by the fact that the future.get()
call supports a timeout), how this be set?看到底层客户端似乎如何支持它(鉴于
future.get()
调用支持超时这一事实),如何设置? The only two properties I can find in the binder references (see here ) are spring.cloud.stream.kafka.binder.healthTimeout
and batchTimeout
, which as far as I can tell, do not influence this setting.我可以在活页夹参考资料中找到的唯一两个属性(参见此处)是
spring.cloud.stream.kafka.binder.healthTimeout
和batchTimeout
,据我所知,这不会影响此设置。
Seeing how the KafkaProducerMessageHandler
is constructed in org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder.ProducerConfigurationMessageHandler
, a private class, bean overridding is seemingly not the recommended way.看看
org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder.ProducerConfigurationMessageHandler
中KafkaProducerMessageHandler
是如何构建的,一个私有的 class,bean 覆盖似乎不是推荐的方式。
It doesn't appear to be documented, but similar to the listener container customizer https://docs.spring.io/spring-cloud-stream/docs/3.1.2/reference/html/spring-cloud-stream.html#_advanced_consumer_configuration you can add a ProducerMessageHandlerCustomizer
@Bean
to set arbitrary properties on the message handler.它似乎没有记录,但类似于侦听器容器定制器 https://docs.spring.io/spring-cloud-stream/docs/3.1.2/reference/html/spring-cloud-stream.html# _advanced_consumer_configuration你可以添加一个
ProducerMessageHandlerCustomizer
@Bean
来设置消息处理程序的任意属性。
In newer versions of the handler, the timeout is always configured to be at least as much as ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG
, to avoid false negatives (where the publication is successful after the handler times it out).在较新版本的处理程序中,超时始终配置为至少与
ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG
一样多,以避免误报(在处理程序超时后发布成功)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.