Spring Integration使用<jms:inbound-gateway>处理并发请求时的会话关闭问题
解决Spring Integration JMS入站网关并发下Session Close异常问题
我之前在处理Spring Integration JMS网关的并发场景时,也碰到过一模一样的问题,咱们来一步步拆解解决它:
为啥会出现这个问题?
核心原因是异常传播把JMS会话提前搞关了,连累了其他正常请求:
- 当某个请求没带
replyTo头时,后续处理逻辑肯定会抛异常,如果这个异常没被及时拦截,Spring Integration就会把当前的JMS会话给关掉 - 在并发场景下,很多请求会复用连接池里的会话实例,要是一个异常请求把会话关了,后面正常请求再用这个会话发回复,自然就会报Session Close的错
具体怎么解决?
1. 提前拦掉没带replyTo的无效请求
在处理流程最前面加个拦截器,先检查replyTo头有没有,没有就直接打日志丢弃,别让它进正常流程搞事情:
public class ReplyToValidationInterceptor implements ChannelInterceptor { private static final Logger log = LoggerFactory.getLogger(ReplyToValidationInterceptor.class); @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { Destination replyTo = message.getHeaders().get(JmsHeaders.REPLY_TO, Destination.class); if (replyTo == null) { log.warn("收到不带replyTo头的JMS消息,直接丢弃"); return null; } return message; } }
然后把这个拦截器配置到入站网关的输入通道上:
<int:channel id="jmsInboundChannel"> <int:interceptors> <int:ref bean="replyToValidationInterceptor"/> </int:interceptors> </int:channel> <jms:inbound-gateway request-channel="jmsInboundChannel" connection-factory="jmsConnectionFactory" request-destination="requestQueue"/>
2. 给异常加个“安全垫”,别让它搞垮会话
给JMS入站网关配个全局异常处理器,把所有异常都接住,别让它们传到JMS容器层导致会话被强制关闭:
<int:chain input-channel="jmsInboundChannel"> <int:service-activator ref="yourMessageHandler"/> <int:exception-strategy ref="jmsErrorHandler"/> </int:chain> <bean id="jmsErrorHandler" class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer"> <constructor-arg ref="errorChannel"/> </bean> <int:channel id="errorChannel"/> <int:service-activator input-channel="errorChannel" ref="errorLogger"/>
对应的错误日志处理器可以这么写,还能顺便处理有replyTo的异常请求,给人回个错误消息:
@Component("errorLogger") public class ErrorLogger { private static final Logger log = LoggerFactory.getLogger(ErrorLogger.class); public void handleError(ErrorMessage errorMessage) { log.error("处理JMS消息出错", errorMessage.getPayload()); // 如果消息带了replyTo,就给人回个错误响应 Destination replyTo = errorMessage.getHeaders().get(JmsHeaders.REPLY_TO, Destination.class); if (replyTo != null) { // 这里可以构造错误回复并发送到replyTo地址 } } }
3. 让会话“各用各的”,避免互相连累
如果你用了JMS连接池,可以配置成每个请求用独立的会话,这样一个请求出问题,不会影响其他请求的会话:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> <property name="targetConnectionFactory" ref="yourTargetJmsConnectionFactory"/> <property name="sessionCacheSize" value="10"/> <!-- 关闭消费者缓存,每个请求用新会话 --> <property name="cacheConsumers" value="false"/> </bean>
另外,如果你的场景不需要事务,也可以给入站网关加个session-transacted="false",这样异常不会触发会话回滚关闭。
最后再提个醒
- 一定要把所有异常都接住,别让未处理的异常跑到JMS容器层,那时候会话肯定会被强制关掉
- 对于没带
replyTo的请求,处理逻辑要明确:是直接丢,还是打日志,千万别让它进到正常处理流程里捣乱
内容的提问来源于stack exchange,提问作者Mahesh Jadhav
相关产品推荐
相关产品推荐

