Spring JmsListener内调用JmsTemplate.send消息延迟送达问题排查
问题原因及无代码修改的配置方案
原因分析
- Spring的
@JmsListener默认在同一个JMS会话线程内处理消息,即便没显式声明事务,JMS客户端的本地会话事务(Auto-Commit模式)也会生效。会话内的所有操作(包括接收消息、发送新消息)都要等Listener方法执行完毕、会话提交后才会真正生效。BE在Listener里调用jmsTemplate.send的消息会被缓存到当前会话,直到Listener执行完成才发送,所以Gateway要等BE的Listener执行结束才能收到消息。 - 默认情况下
JmsTemplate会复用当前线程绑定的Listener会话,发送操作被纳入该会话上下文,受会话提交时机限制。
无代码修改的配置方案
1. 修改JmsListener容器工厂,分离会话
调整DefaultJmsListenerContainerFactory的缓存级别和事务设置,让发送操作使用独立会话:
@Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setSessionTransacted(false); factory.setCacheLevel(CacheLevel.NONE); return factory; }
XML配置方式:
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.listener.DefaultJmsListenerContainerFactory"> <property name="connectionFactory" ref="connectionFactory"/> <property name="sessionTransacted" value="false"/> <property name="cacheLevel" value="0"/> <!-- 对应CacheLevel.NONE --> </bean>
原理:cacheLevel=NONE会让Listener每次处理消息都创建新会话,JmsTemplate也会使用独立会话发送消息,发送操作不再受Listener会话提交时机约束,消息会立即发送。
2. 单独配置JmsTemplate使用独立会话
如果不想改动Listener容器工厂,可单独配置JmsTemplate强制使用新会话:
@Bean public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) { JmsTemplate template = new JmsTemplate(connectionFactory); template.setSessionTransacted(false); template.setCacheLevel(CacheLevel.NONE); return template; }
原理:禁用会话复用后,JmsTemplate每次发送都会创建新的连接和会话,发送操作会立即提交,消息直接送达MQ。
3. 调整MQ客户端的异步发送设置
以ActiveMQ为例,修改连接工厂参数启用异步发送:
@Bean public ConnectionFactory connectionFactory() { ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616"); factory.setUseAsyncSend(true); return factory; }
原理:开启异步发送后,客户端无需等待会话提交,消息会立即发送到MQ服务器,不受Listener方法执行周期影响。
内容的提问来源于stack exchange,提问作者user3027786
相关产品推荐
相关产品推荐

