这里是小奏 ,觉得文章不错可以关注公众号小奏技术
RocketMQ version
- 5.1.0
背景
- 线上RocketMQ偶尔出现从Nameserve获取元数据TimeOut
java
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
之前排查过一次,可以参考线上RocketMQ偶发sendDefaultImpl call timeout问题排查及优化方向总结:mp.weixin.qq.com/s/bPxq-knvO...
- 查看Nameserve发现打印大量
NETTY CLIENT PIPELINE: IDLE exceptionlog
初步解决方案
最开想的是既然超时了,那就直接增加超时时间,优先让程序正常运行。
client增加如下配置
java
producer.setSendMsgTimeout(8000);
实际早起出现过类似的问题,超时时间由默认3s调整为5s了。现在暂时调整为8s看能不能解决这个问题
实际结果是增加了超时时间还是会出现TimeOut问题
由于之前分析过出现sendDefaultImpl call timeout 是还没发送消息就超时了,所以这里重点关注Nameserve
问题排查
通过阅读源码发现几个问题
client会定时去扫描所有Nameserve并与所有Nameserve建立连接
java
int connectTimeoutMillis = this.nettyClientConfig.getConnectTimeoutMillis();
TimerTask timerTaskScanAvailableNameSrv = new TimerTask() {
@Override
public void run(Timeout timeout) {
try {
NettyRemotingClient.this.scanAvailableNameSrv();
} catch (Exception e) {
LOGGER.error("scanAvailableNameSrv exception", e);
} finally {
timer.newTimeout(this, connectTimeoutMillis, TimeUnit.MILLISECONDS);
}
}
};
this.timer.newTimeout(timerTaskScanAvailableNameSrv, 0, TimeUnit.MILLISECONDS);
- 但是
client并不会主动给Nameserve发送心跳 - netty通信模块
client和server都配置了空闲检测
- client
- server
对
Netty心跳检测机不熟悉的朋友可以参考我之前的文章 Netty心跳检测机制实战(附源码):mp.weixin.qq.com/s/-luniYo8v...
- nameserver只会定时30s从单个
Nameserve获取元数据,这个操作也就是充当了client和Nameserve的心跳机制
java
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
MQClientInstance.this.updateTopicRouteInfoFromNameServer();
} catch (Exception e) {
log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);
}
}, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS);
问题定位
经过上面的源码分析就很清晰了。我们这里举例说明
现在比如有三个服务order、producer、pay
nameserver有三个节点Nameserve-a、Nameserve-b、Nameserve-c
-
order连接Nameserve-a,与Nameserve-b、Nameserve-c频繁触发NETTY CLIENT PIPELINE: IDLE exception。,Nameserve-b、Nameserve-c为此产生大量NETTY CLIENT PIPELINE: IDLE exception,然后触发频繁的断线重连 -
producer连接Nameserve-b,与Nameserve-a、Nameserve-c频繁触发NETTY CLIENT PIPELINE: IDLE exception。,Nameserve-a、Nameserve-c为此产生大量NETTY CLIENT PIPELINE: IDLE exception,然后触发频繁的断线重连 -
pay连接Nameserve-c,与Nameserve-a、Nameserve-b频繁触发NETTY CLIENT PIPELINE: IDLE exception。,Nameserve-a、Nameserve-b为此产生大量NETTY CLIENT PIPELINE: IDLE exception,然后触发频繁的断线重连
如何修复
定位到问题之后就很好修复了。
实际我们client并不需要与所有nameserver建立连接,仅与单个nameserver建立连接即可。
如果单个nameserver挂了,client会自动切换到其他nameserver上
相关代码
所以修复方式很简单,我们不让client与所有nameserver建立连接,仅与单个nameserver建立连接即可