我正在使用:Spring Boot 1.4.7,Spring Integration 4.3.10,RabbitMQ 3.6.5
我有一个Spring Boot应用程序,它有几个Spring Integration流程,可以向rabbitMQ代理发送和接收消息。
我遇到的问题是,当调用“关闭”执行器时,应用程序并不总是干净地关闭。
执行线程转储后,我可以看到单个“SimpleMessageListenerContainer”线程在对代理的“发送”操作中被阻止:
"org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer#0-1" #81 prio=5 os_prio=0 tid=0x00007fe49bcac800 nid=0x4fc5 waiting on condition [0x00007fe489efe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c259a6f8> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:458)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
at java.util.concurrent.SynchronousQueue.put(SynchronousQueue.java:877)
at org.springframework.integration.channel.QueueChannel.doSend(QueueChannel.java:93)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:358)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:269)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:186)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:188)
at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter.access$1100(AmqpInboundChannelAdapter.java:56)
at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.processMessage(AmqpInboundChannelAdapter.java:246)
at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$Listener.onMessage(AmqpInboundChannelAdapter.java:203)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:823)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:746)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:99)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:191)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1238)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:727)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1192)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1176)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1100(SimpleMessageListenerContainer.java:99)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1370)
at java.lang.Thread.run(Thread.java:745)
根据Gary的反馈更新了信息:
我回顾了所有的频道定义(我们使用Spring集成DSL),其中大部分定义如下:
public MessageChannel channelIdMailOut() {
return MessageChannels.direct().get();
}
然而,我确实发现了几个异常值:
@Bean(name=CHANNEL_NAME_ID_MAIL_IN)
public MessageChannel channelIdMailIn() {
//Using a rendezvous channel on inbound because we use a rest endpoint to pull messages rather than using a push model
return MessageChannels.rendezvous().get();
}
@Bean(name=CHANNEL_NAME_CATEGORY_REFRESH_PRODUCTION_OUT)
public MessageChannel channelCategoryRefreshProductionOut() {
return MessageChannels.publishSubscribe().get();
}
我很欣赏快速反馈,我将进一步探索这条途径。
答案 0 :(得分:2)
at org.springframework.integration.channel.QueueChannel.doSend(QueueChannel.java:93)
看起来您正在使用入站通道适配器下游的有界QueueChannel
...
adapter->DirectChannel->someEndpoint->QueueChannel<-somePoller
......并且队列已满。因为你正在停止上下文;从队列中读取的轮询线程永远不会释放空间。
您没有显示您的配置,但您可以在发送到sendTimeout
的端点上设置QueueChannel
,并且发送将超时。
但是,在这种情况下使用QueueChannel
通常不是一个好主意,除非您不介意消息丢失。关闭时队列中的消息将丢失。
修改强>
在下面回答你的评论。
有几种选择......
RendezvousChannel
的端点添加发送超时;它应该小于入站适配器的侦听器容器上的shutDownTimeout
。taskExecutor
并使用executor.shutDownNow()
(或setWaitForTasksToCompleteOnShutdown(false)
作为Spring执行程序),这将中断尝试发布RC的线程 - 这可能会导致日志中存在一些噪音,因为容器会尝试重新启动使用者线程。stop()
入站通道适配器(应该避免日志噪声)。IntegrationMBeanExporter
并在超时时调用stopActiveComponents()
,以便让事情停顿。对于3和4,如果你可以将spring-rabbit版本提升到1.7.3,你可以使用ApplicationListemer<AsyncConsumerStoppedEvent>
来获得容器线程终止的通知。
答案 1 :(得分:0)
只是跟进对我有用的解决方案。选项#2就像一个魅力。我创建了一个新的任务执行器,并将waitForTasksToCompleteOnShutdown设置为“false”。加里,非常感谢你的帮助!
@Bean
public TaskExecutor taskExecutorIdMailIn(
@Value("${taskExecutor.idMailIn.corePoolSize:4}") int corePoolSize,
@Value("${taskExecutor.idMailIn.maxPoolSize:4}") int maxPoolSize,
@Value("${taskExecutor.idMailIn.queueCapacity:0}") int queueCapacity) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setThreadNamePrefix("taskExecutorIdMailIn-");
taskExecutor.setCorePoolSize(corePoolSize);
taskExecutor.setMaxPoolSize(maxPoolSize);
if (queueCapacity > 0) {
taskExecutor.setQueueCapacity(queueCapacity);
}
taskExecutor.setRejectedExecutionHandler(new CallerRunsPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(false);
return taskExecutor;
}
@Bean
public IntegrationFlow flowRabbitToIdMailIn(ConnectionFactory factory, @Qualifier("taskExecutorIdMailIn") TaskExecutor taskExecutor) {
return IntegrationFlows
.from(Amqp.inboundAdapter(factory, queueNameIdMail)
.taskExecutor(taskExecutor)
.errorHandler(errorHandler)
)
.transform(Transformers.fromJson())
.channel(CHANNEL_NAME_ID_MAIL_IN)
.get();
}