RedisMessageListenerContainer需显式配置destroyMethod="stop"并确保RedisConnectionFactory也声明destroyMethod="destroy",否则应用关闭时监听线程持续运行导致消息重复或连接泄漏。

RedisMessageListenerContainer 不会自动停止监听
Spring Boot 默认不会在应用关闭时主动调用 RedisMessageListenerContainer.stop(),即使你配置了优雅停机(server.shutdown=graceful)。它不属于 Spring 生命周期中默认管理的“可销毁 Bean”,也不会响应 ContextClosedEvent 或 @PreDestroy。结果就是:JVM 进程退出时,监听线程仍在后台运行,可能持续拉取 Stream 消息、ACK 或阻塞在 XREADGROUP 上,导致消息重复消费或连接泄漏。
必须显式注册 destroyMethod="stop"
最直接可靠的方式是声明 RedisMessageListenerContainer 为 @Bean 并指定销毁方法:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
@Bean(destroyMethod = "stop")
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("mystream"));
container.setTaskExecutor(new SimpleAsyncTaskExecutor("redis-listener-"));
return container;
}
-
destroyMethod = "stop"是关键——比@PreDestroy更底层、更确定,确保容器 shutdown 流程中被调用 - 不要手动 new 容器后 set 到其他 Bean 里,否则脱离 Spring 管理,销毁逻辑失效
- 若使用
StreamMessageListenerContainer(对应 Redis Stream),同样适用该模式,只是构造和 topic 注册方式不同
Stream 监听器需配合 pending 消息处理
仅调用 stop() 不足以保证消息不丢失。Stream 的消费组模型要求:关闭前应主动 XAck 或 XClaim 待确认消息,否则重启后可能重复投递。建议在 stop() 前插入清理逻辑:
- 监听器实现
DisposableBean,重写destroy()方法,在其中调用redisTemplate.opsForStream().acknowledge(...) - 或通过
@EventListener监听ContextClosedEvent,在事件中触发一次pending扫描与 ACK - 注意:Lettuce 在
stop()后会中断阻塞读,但已拉取未处理的消息仍需业务层保障幂等或显式 ACK
别忽略连接工厂的销毁顺序
RedisMessageListenerContainer 依赖 RedisConnectionFactory,而后者本身也需要正确销毁(尤其 Lettuce)。如果连接工厂没关干净,监听容器的 stop() 可能抛 IllegalStateException 或静默失败:
- 务必确保
RedisConnectionFactory也声明了destroyMethod = "destroy" - 避免在监听容器 Bean 中直接 new
LettuceConnectionFactory,应注入由自动配置或自定义@Bean提供的实例 - 检查日志是否有
Unable to close connection或Connection is closed类错误——往往是销毁顺序错乱或连接工厂未被管理所致

















