一 环境
- k8s、Spring Boot、Spring Cloud、rabbitmq
二 现象
- mongodb记录数快速猛增,几分钟就达到了几十万
- mongodb所在阿里云机器cpu飙升,没达到预警值,但比平时高很多,明显不正常。
三 问题定位过程
- 初步判定是代码死循环造成
- 查看日志,没看到明显的报错信息
- 快速检查相关插入mongodb的代码,没发现有可能造成死循环的代码
- 通过jstact命令dump出堆栈信息
- 通过命令top -p [PID] -H 快速找到占cpu最高的线程
- 执行命令printf “%x” [线程ID] 转换为16进制
- 查询dump出来的堆栈,查找上一步查询出来的16进制线程ID,找到对应线程
- 通过堆栈快速定位到执行的类和行,从堆栈看出是从消费mq消息开始的,所以初步断定是mq队列消费出现了死循环(rabbitmq消费消息时,如果没捕获异常直接往外抛,不会直接报ERROR,只会报Warn,线上的日志级别非自定义包路径的只到ERROR级别)
- 从上面判断出消费MQ消息是应该是出现了未捕获的异常,加上异常捕获和日志打印,则定位出了具体的原因。
四 总结与反思
问题出现的原因
- rabbitmq使用不规范。某些同学直接使用了RabbitTemplate直接发送mq,直接监听队列@RabbitListener(queues = “xxxx”),只要出现异常消息就永远无法被确认,导致无限循环 消费 -> 出现异常 -> 没确认 -> 再消费 …
- 异常处理没做好。在消费消息的最上层仅捕获了业务异常,其他异常一概不管。
建议
建议使用Cloud集成rabbitmq的Stream方式做rabbitmq的消息发送与消费。该方式默认消费消息最多重试3次,指定了group后,多个节点之间topic消息也不至于出现重复消费的情况。