1. 背景
我们的mqtt服务线上一实例出现OOM,通过日志发现,OOM具体原因是因为没有足够空间创建新线程。
image.png
通过监控发现,该实例线程数一直缓慢增长,并没有回收。
image.png
2. 分析线程文件
jstack命令生成线程dump文件,通过分析,发现MQTT客户端创建了大量线程,并且没有释放。
image.png
3. 源码分析
服务使用的mqtt客户端如下
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
使用方式如下:
image.png
主要问题出在对mqtt client连接处理的代码上。原有代码的连接过程大致如下:
新建一个MqttClient对象 --> 设置callback监听断连、收到消息和发送消息成功的回调逻辑 --> 调用connect()方法连接emqx broker --> 订阅topic。
在收到断连的回调消息后,会发起重连操作,把上述流程重走一遍
image.png
排查线上日志发现有不少断连,
image.png
问题在于每一次重连后,老的MqttClient对象没有close()掉。
一个client又会引用着4种线程,
1、ConnectBG:负责连接操作的
2、CommsReceiver:用于消息接收
3、CommsSender:用于消息发送
4、CommsCallback:执行回调通知
image.png
因此随着重连次数增加, 线程数也缓慢叠加。
4. 解决方案
断连后,旧的Mqttclient调用close()方法,关闭资源
5. 效果
发生重连前:
image.png
借用tcpkill,模拟关闭tcp连接。
image.png
日志显示已经发生了重连
image.png
重连前后的MQTT线程,旧的线程已经成功回收。
image.png
发布后线上环境的效果
image.png