RocketMQ version
5.1.0
背景
線上RocketMQ偶爾出現從Nameserve獲取後設資料TimeOut
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
檢視Nameserve發現列印大量
NETTY CLIENT PIPELINE: IDLE exception
log
初步解決方案
最開想的是既然超時了,那就直接增加超時時間,優先讓程式正常執行。
client增加如下配置
producer.setSendMsgTimeout(8000);
實際早起出現過類似的問題,超時時間由預設3s調整為5s了。現在暫時調整為8s看能不能解決這個問題
實際結果是增加了超時時間還是會出現TimeOut
問題
由於之前分析過出現sendDefaultImpl call timeout
是還沒傳送訊息就超時了,所以這裏重點關注Nameserve
問題排查
透過閱讀原始碼發現幾個問題
client
會定時去掃描所有Nameserve
並與所有Nameserve
建立連線
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
nameserver只會定時30s從單個
Nameserve
獲取後設資料,這個操作也就是充當了client
和Nameserve
的心跳機制
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
建立連線即可