Spring Cloud Bus RabbitMQ AMQP客户端长时间闲置后连接断开问题
Spring Cloud Bus + RabbitMQ 长时间闲置后连接断开问题解决
环境版本
- RabbitMQ: 3.6.14(Docker镜像
rabbitmq:3.6.14-management-alpine) - Java: 1.8
spring-cloud-starter-bus-amqp: 1.3.1.RELEASE- Spring Boot父版本: 1.5.9.RELEASE
问题描述
使用Spring Boot服务结合RabbitMQ与Spring Cloud Bus搭建远程配置刷新功能,初始运行一切正常,但长时间闲置后连接会断开,出现心跳丢失和队列资源锁定的错误。
错误日志
2018-05-22 17:10:49.647 ERROR 9 --- [.X.X.228:5671] c.r.c.impl.ForgivingExceptionHandler : 发生意外的连接驱动错误 com.rabbitmq.client.MissedHeartbeatException: 心跳丢失,心跳间隔为10秒 at com.rabbitmq.client.impl.AMQConnection.handleSocketTimeout(AMQConnection.java:723) at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:642) at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47) at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:572) at java.lang.Thread.run(Thread.java:745) 2018-05-22 17:10:49.652 ERROR 9 --- [.X.X.228:5671] o.s.a.r.c.CachingConnectionFactory : 通道关闭:连接错误 2018-05-22 17:10:49.652 ERROR 9 --- [.X.X.228:5671] o.s.a.r.c.CachingConnectionFactory : 通道关闭:连接错误 2018-05-22 17:10:50.581 INFO 9 --- [NiW8BAjOWTiVA-1] o.s.a.r.l.SimpleMessageListenerContainer : 重启消费者@574b7f4a: tags=[{amq.ctag-m35NYazOB0vmhjJHwayDMg=springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA}], channel=Cached Rabbit Channel: AMQChannel(amqp://admin@10.X.X.228:5671/,1), conn: Proxy@76ad6715 Shared Rabbit Connection: SimpleConnection@5a1c3cb4 [delegate=amqp://admin@10.X.X.228:5671/, localPort= 43472], acknowledgeMode=AUTO local queue size=0 2018-05-22 17:10:50.609 INFO 9 --- [NiW8BAjOWTiVA-2] o.s.a.r.c.CachingConnectionFactory : 创建新连接:rabbitConnectionFactory#934b52f:1/SimpleConnection@3cd111c6 [delegate=amqp://admin@10.X.X.78:5671/, localPort= 50792] 2018-05-22 17:10:50.613 INFO 9 --- [NiW8BAjOWTiVA-2] o.s.amqp.rabbit.core.RabbitAdmin : 自动声明非持久化、自动删除或独占队列(springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA),属性:durable:false, auto-delete:true, exclusive:true。若代理停止并重启时连接工厂仍存活,该队列会被重新声明,但所有消息会丢失。 2018-05-22 17:10:50.617 ERROR 9 --- [0.X.X.78:5671] o.s.a.r.c.CachingConnectionFactory : 通道关闭:通道错误;协议方法:#method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - 无法获取虚拟主机'/'中已锁定队列'springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA'的独占访问权限, class-id=50, method-id=10) 2018-05-22 17:10:50.619 WARN 9 --- [NiW8BAjOWTiVA-2] o.s.amqp.rabbit.core.RabbitAdmin : 声明队列失败:Queue [name=springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA, durable=false, autoDelete=true, exclusive=true, arguments={}], 继续执行... com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - 无法获取虚拟主机'/'中已锁定队列'springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA'的独占访问权限, class-id=50, method-id=10) 2018-05-22 17:10:50.621 ERROR 9 --- [0.X.X.78:5671] o.s.a.r.c.CachingConnectionFactory : 通道关闭:通道错误;协议方法:#method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - 无法获取虚拟主机'/'中已锁定队列'springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA'的独占访问权限, class-id=50, method-id=20) 2018-05-22 17:10:50.623 WARN 9 --- [NiW8BAjOWTiVA-2] o.s.amqp.rabbit.core.RabbitAdmin : 声明绑定失败:Binding [destination=springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA, exchange=springCloudBus, routingKey=#], 继续执行... com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - 无法获取虚拟主机'/'中已锁定队列'springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA'的独占访问权限, class-id=50, method-id=20) 2018-05-22 17:10:50.629 ERROR 9 --- [0.X.X.78:5671] o.s.a.r.c.CachingConnectionFactory : 通道关闭:通道错误;协议方法:#method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - 无法获取虚拟主机'/'中已锁定队列'springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA'的独占访问权限, class-id=50, method-id=10) 2018-05-22 17:10:50.630 WARN 9 --- [NiW8BAjOWTiVA-2] o.s.a.r.listener.BlockingQueueConsumer : 声明队列失败:springCloudBus.anonymous.eV7zCVLlSNiW8BAjOWTiVA 2018-05-22 17:10:50.631 WARN 9 --- [NiW8BAjOWTiVA-2] o.s.a.r.listener.BlockingQueueConsumer : 队列声明失败
问题分析
- 心跳丢失根源:日志显示心跳间隔为10秒,长时间闲置时,网络防火墙/负载均衡可能会主动切断空闲连接,导致客户端无法收到RabbitMQ的心跳包,触发
MissedHeartbeatException,最终连接断开。 - 队列锁定问题:Spring Cloud Bus默认使用匿名独占队列(
springCloudBus.anonymous.xxx),这类队列的exclusive属性为true,意味着只能被当前连接访问。当旧连接因心跳超时断开后,RabbitMQ可能不会立即释放队列的独占锁,此时客户端重连尝试重新声明同一队列,就会触发RESOURCE_LOCKED错误。
解决方案
1. 优化RabbitMQ连接与心跳配置
调整Spring Boot配置,让连接更稳定,避免因闲置断开:
spring: rabbitmq: connection-timeout: 60000 # 连接超时设为60秒 requested-heartbeat: 30 # 客户端请求心跳间隔30秒,平衡闲置检测与网络开销 cache: channel: size: 5 # 缓存通道,减少频繁创建开销 connection: mode: cache # 启用连接缓存
2. 自定义Spring Cloud Bus队列属性,禁用独占模式
替换默认的匿名独占队列,使用固定名称的非独占队列:
spring: cloud: bus: queue: name: spring-cloud-bus-refresh # 固定队列名称 durable: true # 持久化队列,防止重启丢失 auto-delete: false # 关闭自动删除 exclusive: false # 禁用独占属性,避免锁冲突
这样重连时无需争抢独占锁,彻底解决RESOURCE_LOCKED问题。
3. 启用消费者重试机制
配置Spring AMQP的重试策略,给RabbitMQ足够时间释放旧锁,自动恢复连接:
spring: rabbitmq: listener: simple: retry: enabled: true # 开启重试 max-attempts: 5 # 最多重试5次 initial-interval: 2000 # 初始重试间隔2秒 multiplier: 1.5 # 间隔每次乘以1.5 max-interval: 10000 # 最大重试间隔10秒
额外建议
- 检查网络设备(防火墙/负载均衡)的空闲连接超时时间,确保大于RabbitMQ的心跳间隔(比如设为5分钟以上)。
- 考虑升级RabbitMQ到3.8+版本,旧版本在独占队列锁释放逻辑上存在一些已知问题,新版本修复了相关bug。
- Docker部署RabbitMQ时,确保容器网络配置稳定,避免因网络波动导致的连接中断。
内容的提问来源于stack exchange,提问作者turbocoder
相关产品推荐
相关产品推荐

