1.业务与环境:
环境:springboot2.2.6.release+rocketMQ4.6+mysql5.6+Linux 4核8G内存
业务:定时任务查询mysql的list数据,使用多线程发送消息,
定时任务使用 @Scheduled 每5秒执行一次
多线程使用的是list.parallelStream()方式
发送消息Producer使用 DefaultMQProducer producer = new DefaultMQProducer();
producer.start();每次发完消息后 froducer.shutdown();
2.情况说明
线上部署大约一天后,发现数据发送比较慢,有延迟,查看日志,发现 数据处理、入库等操作消耗时间为5-60s;内存也使用彪到99%,cpu使用率是正常的。
重启后发现情况好转,但一天后又出现这种情况;
3.找问题
1.使用 jps -l 查看该服务的pid
2.使用jstack 导出线程使用情况,命令为
jstack -l pid > jstack.txt
3.打开jstack.txt,发现
"RequestHouseKeepingService" #95300 daemon prio=5 os_prio=0 tid=0x00007fc89271a000 nid=0x518 in Object.wait() [0x00007fc6493bc000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.util.TimerThread.mainLoop(Timer.java:552)
- locked <0x00000000e6b5cb98> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
Locked ownable synchronizers:
- None
RequestHouseKeepingService线程有9000+,而且都是TIMED_WAITING (on object monitor)状态,
RequestHouseKeepingService 为生产者 MQProducerInner的实现类 DefaultMQProducerImpl 下的一个属性
private final Timer timer = new Timer("RequestHouseKeepingService", true);
Timer是线程安全的,每次请求都会创建一个新的线程;
timer的线程被挂起,无线等待;猜测可能是 producer 一直在启动/关闭,timer的任务未执行完毕被挂起;
4.解决方法
使用springboot的配置文件+注入方式 注入生产者
@Autowired
DefaultMQProducer defaultMQProducer;
rocketmq:
producer:
group: GROUP_TEST
name-server: 127.0.0.1:9876
不在频繁创建/启动/关闭 DefaultMQProducer ;
重新部署服务,监测运行一周,内存99%情况没有再次出现;
5.结论
此次事故是RocketMQ的生产者使用不当导致,RocketMQ还需要多多学习;