这里是weihubeats ,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党
缘起
访问线上的RocketMQ
集群的dashboard
突然上不去了提示超时
初次排查
看报错很清晰可以看到就是timeout
所以最简单的是增加了超时时间(我多等一会),从5s
增加到10s
,15s
但是发现并没有什么用,还是超时
这时候初步怀疑是网络问题
查看RocketMQ log
这时候没什么其他办法,只能去服务器上面看看有没有错误log
发现服务器存在大量报错log
java
at io.netty.channel.AbstractChannel$AbstractUnsafe.write(ObjectChannelPromise)(Unknown Source)
08-18 19:43:53 ERROR NettyServerNIOSelector_3_2 - Failed to write ressponse[request code: 41, response code: 0, opaque: 166429356] to chanel[id: 0xc0b80baf, L:/192.168.1:30911
R:/192.168.2:56824]
这里可以看到的错误就是RocketMQ
broker
一直想要将client
请求的数据响应给client
,但是client
都没有接受这个响应,因为broker
与client
之间的连接已经关闭了 大致流程如下
所以这里看到的情况就是服务端一直在报错
client
一直向broker
发送请求,但是这些请求broker
处理了,client
却在接收响应前断开连接了
这个错误会导致dashboard
连接不上吗?单看这个log并不能看出问题,我们再看看RocketMQ
的remote.log
文件看看,发现有大量的channelGegistered
java
2023-08-18 20:26:57 INFO NettyServerCodecThread_8 - NETTY SERVER PIPELINE: channelRegistered 192.168.1.12:60912
2023-08-18 20:26:58 INFO NettyServerCodecThread_6 - NETTYSERVER PIPELINE: channelRegistered 192.168.1.12:60914
2023-08-18 20:26:59 INFO NettyServerCodecThread_5 - NETTYSERVER PIPELINE: channelRegistered 192.168.1.12:609968
2023-08-18 20:27:00 INFO NettyServerCodecThread_4 - NETTYSERVER PIPELINE: channelRegistered 192.168.1.12:58374
2023-08-18 20:27:01 INFO NettyServerCodecThread_2 - NETTY SERVER PIPELINE: channelRegistered 192.168.1.12:37832
这里发现有个问题,有个客户端192.168.1.12
一直在重复注册,也就是重复建立连接
猜测可能是BossGroup被打满一直占用着
RocketMQ 线程池模型
我们知道RocketMQ
的远程通信线程模型是 1+N+M+M
这一块的线程模型可以给大家看看源码
可以看到boosGroup
是一个
这里可以看到workGroup
是多个,实际在wrokGroup
还有再分多个线程池去处理不同的业务逻辑
由于boosGroup
是一个,所以存在boosGroup
被打满的情况,导致dashboard
连接不上超时
查看linux tcp连接情况
为了确认这个猜想,我这里查看了linux机器上的tcp连接数量
arduino
watch -n 1 'netstat -nat | grep ESTABLISHED | wc -l'
会发现打印出来的tcp
连接数量一直在变化(一直增加又减少),说明一直在断线重连
断线重连问题排查
为什么client
要一直断线重连呢
这里我们查看源码发现client
如果发生超时,就会断线。再次请求就需要重新建立连接
我们去查看broker
的log
也发现了大量的关闭socket
log
perl
invokeSync: close socket because of timeout
阿里云 client sdk与开源版本的差异
我们查看源码发现阿里云商业版本的sdk如果超时并不会断开连接
所以只有开源版本才会出现这个问题。
为了解决断线重连的问题,所以client
添加如下配置
arduino
System.setProperty(COM_ROCKETMQ_REMOTING_CLIENT_CLOSE_SOCKET_IF_TIMEOUT, "false");
client断线重连解决
添加参数后会发现不存在client
断线重连的log,但是还有如下问题
dashboard
还是偶尔能连接上client
的超时问题并没有解决,只是解决了超时不断线重连- 从
broker
仍然存在断线重连
这里还是以为BossGroup
被打满
由于线上的RocketMQ集群主从同步之间仍然存在断线重连
其次线上RocketMQ 集群不能随便重启,也不能随便修改代码。所以不打算添加client
之前添加的配置
java
System.setProperty(COM_ROCKETMQ_REMOTING_CLIENT_CLOSE_SOCKET_IF_TIMEOUT, "false");
再次怀疑是网络问题
这次猜想是可能是主从机器之间网络出了问题
为了更加严谨快速验证,打算用goland
写一个client
和server
进行tcp
连接,然后仅发送心跳
server
核心代码如下
结果验证
可以看到网络耗时都在1ms
左右,所以其实两台机器网络都没问题
怀疑是堆外内存不够、serverSelectorThreads、serverWorkerThreads不够
检查线上的RocketMQ
部署实际给到的jvm内存为宿主机的90%,但是RocketMQ
使用使用了大量的堆外内存。
其次在网上看到一篇博文也是遇到类似的问题说是 serverSelectorThreads
和serverWorkerThreads
线程池不够
所以又对集群进行了优化
- 优化JVM启动参数
- 优化Netty bossGroup为EpollEventLoopGroup
- 增大serverSelectorThreads和serverWorkerThreads
然后重启部署
结果: 问题还是没解决
排除网络问题
每天茶饭不思,突然突发灵感。
我在broker
这台机器发送请求自己,如果响应还是超时,那么就可以确定不是网络问题
在broker执行如下脚本
shell
./mqadmin getBrokerConfig -n 127.0.0.1:9000 -c xiaozou
发现还是broker
自己请求自己 还是超时
所以可以确定一定不是网络问题
是否gc导致的
检查服务端gc
、cpu
、内存
请求
发现都是非常正常,且低频的,没有任何异常
arthas加入战场
为了定位到到底是哪里导致的超时,使用了arthas
进行了方法耗时追踪
发现整个请求基本在240ms就完成了,根本不会超过5s
抓取整个逻辑代码的处理
发现代码的耗时只要是在异步转同步
这里实际是又开了一个线程池去处理请求
也就就是1+n+m里面的M
这里初步怀疑是M线程池(AdminBrokerThread_)
满了导致的
随即使用arthas
抓取线程池里面的任务
css
vmtool --action getInstances --className org.apache.rocketmq.broker.latency.BrokerFixedThreadPoolExecutor --limit 100 --express "instances[5].workQueue.{#this.runnable}" -x 2
然后发现大量堆积任务
再次使用arthas
看看queue里面堆积了多少个任务
css
vmtool --action getInstances --className org.apache.rocketmq.broker.latency.BrokerFixedThreadPoolExecutor --limit 100 --express "instances[5]"
发现workQueue
还在一直增加
总结
这里真相水落石出了,主要是还是(AdminBrokerThread_)
线程池被打满堆积了太多任务导致的
如何处理呢
- 增大
(AdminBrokerThread_)
线程池 - 减少对
(AdminBrokerThread_)
线程池的请求 (AdminBrokerThread_)
线程池添加监控- broker添加相关的请求限流