Spring Boot应用中@JMSListener无异常无报错停止监听事件的问题咨询
解决JMS监听无报错停止消费的问题
遇到JMS监听突然停止、又没有任何异常日志的情况确实让人困惑,结合你的代码配置、日志输出和线程信息,我整理了几个核心排查点和解决方案:
1. 给JMS容器加上异常防护机制
你的@JMSListener设置了concurrency = "1-2",但配置的DefaultJmsListenerContainerFactory缺少关键的异常处理和消息确认配置。默认情况下,如果消费逻辑(比如推Kafka的过程)抛出未捕获的异常,容器会直接终止该监听线程,而且不会主动重启,看起来就像“悄无声息停止了”。
修复方案:
更新你的容器工厂配置,添加异常处理器、确认模式和自动恢复策略:
@Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws JMSException { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory()); // 手动确认消息,避免自动确认导致消息丢失 factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); // 捕获消费异常并打印日志,防止线程直接挂掉 factory.setErrorHandler(throwable -> { log.error("JMS消费过程出现异常,将继续监听", throwable); }); // 配置线程异常后的自动恢复间隔(5秒后尝试重启线程) factory.setRecoveryInterval(5000L); return factory; }
同时,在你的监听方法里手动确认消息(配合上面的CLIENT_ACKNOWLEDGE模式),并捕获所有异常:
@JMSListener(destination = "myDestination", concurrency = "1-2") public void consumeMessage(Message message) { log.info("开始消费JMS消息,ID: {}", message.getJMSMessageID()); try { // 推送Kafka的逻辑 kafkaProducer.send(...).get(); // 手动确认消息,确保消息处理完成后再告诉EMS message.acknowledge(); log.info("消息消费并推送Kafka完成,ID: {}", message.getJMSMessageID()); } catch (Exception e) { log.error("处理消息失败,ID: {}", message.getJMSMessageID(), e); // 这里可以根据业务逻辑选择重投或转发到死信队列 } }
2. 排查Kafka生产者的阻塞风险
从日志看,最后一条消息成功推送到Kafka了,但之后没有新的消费日志。要确认你的Kafka生产者是否存在阻塞情况:
- 如果用的是同步发送(比如调用了
.get()),一旦Kafka集群出现延迟或不可达,可能会导致消费线程被长时间阻塞,看起来就像监听停止了 - 检查Kafka生产者的
acks、timeout配置是否合理,避免无限等待
验证建议:
在推送Kafka的代码里添加超时控制,比如:
kafkaProducer.send(record).get(10, TimeUnit.SECONDS); // 设置10秒超时
3. 分析线程状态定位问题
从线程转储信息来看,重点关注JMS监听线程的状态:
- 找到
DefaultMessageListenerContainer相关的线程,看是否处于WAITING或BLOCKED状态 - 如果线程处于
WAITING,可能是EMS队列暂时没有新消息,但如果是永久停止,更可能是线程因异常终止 - 如果线程处于
BLOCKED,要排查是否有锁竞争或者外部资源(比如数据库、Kafka)的阻塞问题
4. 给Tibco EMS连接加上重连保障
你的TibjmsConnectionFactory只配置了基础的连接信息,没有设置重连策略。如果EMS服务器临时断开连接,默认配置不会自动重连,导致监听线程无法恢复。
优化配置:
添加EMS的重连和超时配置:
final TibjmsConnectionFactory factory = new TibjmsConnectionFactory(); factory.setServerUrl(url); factory.setUserName(userName); factory.setUserPassword(password); // 开启自动重连,最多尝试10次,每次间隔5秒 factory.setReconnectAttempts(10); factory.setReconnectInterval(5000); // 设置连接超时时间,避免无限等待 factory.setConnectionTimeout(30000);
内容的提问来源于stack exchange,提问作者Ankit Kadam
相关产品推荐
相关产品推荐

