停止Spring JMS监听器在ActiveMQ Classic中引发异常行为
问题场景与现象
代码实现
JMS监听器定义
@JmsListener(id="myid", destination = "myDestination") public void SampleJmsListenerMethod(String text) { System.out.println("1: "+text); } @JmsListener(id="myid2", destination = "myDestination") public void SampleJmsListenerMethod2(String text) { System.out.println("2: "+text); }
启停控制REST接口
@RequestMapping(value="/halt", method= RequestMethod.GET) public @ResponseBody String haltJmsListener() { JmsListenerEndpointRegistry customRegistry = context.getBean(JmsListenerEndpointRegistry.class); Objects.requireNonNull(customRegistry.getListenerContainer("myid")).stop(); return "Jms Listener Stopped"; } @RequestMapping(value="/restart", method=RequestMethod.GET) public @ResponseBody String reStartJmsListener() { JmsListenerEndpointRegistry customRegistry = context.getBean(JmsListenerEndpointRegistry.class); Objects.requireNonNull(customRegistry.getListenerContainer("myid")).start(); return "Jms Listener restarted"; }
观测到的行为
- 应用启动后:存在2个连接、2个活跃消费者,消息以轮询方式被两个监听器消费。
- 停止
myid监听器后:控制台仍显示2个连接和消费者,但仅能收到每隔1条的消息,其余消息滞留在队列中。 - 重启
myid监听器后:会先消费积压的消息,之后恢复轮询消费。 - 版本差异:
- 问题复现版本:ActiveMQ Classic 5.15(搭配Spring 2.3.12)、5.16(搭配Spring 2.7.8)
- 正常表现版本:Spring 3.2.1 + ActiveMQ 5.18,停止监听器后剩余监听器会消费所有消息,且对应连接会被移除。
疑问
该旧版本中的异常行为是否属于预期情况?
结论
这种旧版本中的行为不属于预期情况,是Spring JMS和ActiveMQ Classic旧版本组合下的实现缺陷:
- 在旧版本中,调用
stop()方法停止监听器容器时,Spring JMS并未正确关闭对应JMS连接和消费者,导致ActiveMQ端仍认为该消费者存在,依旧会将消息分发给这个已失效的消费者,从而造成消息滞留。 - Spring 3.x+和ActiveMQ 5.18的组合中,Spring JMS优化了监听器容器停止时的资源清理逻辑,同时ActiveMQ也完善了消费者失效的检测机制,能够正确移除失效的消费者连接,剩余消费者会接管所有消息的消费,这才是符合预期的行为。
内容的提问来源于stack exchange,提问作者Pieter
相关产品推荐
相关产品推荐

