hello,各位小夥伴們,上午好~前端
昨晚生產系統機房切換,又度過了一個不眠之夜。趁着這段無聊時間,分享一下前一段時間 RocketMQ 踩坑經歷java
太慘了!!!早上剛躺下睡了兩小時,就被一通電話僥倖起來查看問題。
事情是這樣的,前端時間咱們有個新業務上線,這個業務須要監聽支付成功的 mq 消息,而後向綁定的音箱推送消息。這樣用戶在支付完成以後,商家端就就能夠收到收款播報。git
起初咱們在測試環境的測試的時候,一切流程很是順利,沒有任何問題。可是等到咱們發佈上線以後,卻出現了問題。 github
一筆支付成功以後,音箱沒有發出收款成功的播報。一切流程排查下來以後,這才發現原來 MQ 消費端沒有正常在消費消息。apache
開始排查問題,第一想到的是消費端是否是發佈失敗了,可是查看相關日誌,並無任何異常。安全
登陸 MQ 控制檯,嘗試手動從新發布消息,神奇的事來了,消費端成功收到消息。網絡
總結如今的問題,下文開始排查。異步
剛開始排查的時候,因爲沒有任何異常業務日誌能夠定位問題,因此問題排查起來十分困難。ide
排查了兩天了,想過各類問題。好比當前消費端使用 RocketMQ 客戶端版本比較高,是否是版本兼容性致使的問題呢?源碼分析
因而下降消費端的版本,從新發布以後,問題依然存在。
沒辦法,只好使用 Google 大法了。
經過搜索發現,原來默認狀況下 rockmq 客戶端的日誌將會單獨打印輸出,日誌文件位置以下:
${user.home}/logs/rocketmqlogs
下圖爲當時的日誌截圖:
能夠看到消費端嘗試鏈接一個 20878 的端口,可是因爲網絡問題,一直鏈接失敗。
那這個 20878 是什麼端口?
咱們並無主動配置這個端口,可是 rocketmq broker 配置的端口爲 20880。
搜索發現,原來 rocketmq broker 默認將會啓動三個通信端口:
第一個是 rocketmq broker 配置文件上配置的端口,默認端口爲 10911,這裏咱們修改爲了 20880。
第二個是 rockemq broker vip 通道端口,這個端口將會在第一個端口基礎上減 2,即 20878。
第三個是 rockemq broker 用戶主從數據同步的端口,這個端口將會在第一個端口基礎上加 1,即 20881。
大概知道問題,解決辦法就很簡單了,要麼防火牆打開 29878 網絡端口的限制,要麼關閉使用 vip 端口。
RocketMQ 客戶端提供兩種方式關閉使用 vip 端口。
## 消費端 DefaultMQPushConsumer#setVipChannelEnabled(false) ## 生產端 DefaultMQProducer#setVipChannelEnabled(false);
-Dcom.rocketmq.sendMessageWithVIPChannel=false
雖然問題解決了,可是上述問題本質緣由尚未找到。因此此次咱們就從源碼出發,追本溯源。
從 rocketmq 錯誤日誌,咱們能夠看到報錯代碼位於 RebalanceService
類中。
這裏主要用來執行 topic Rebalance(重平衡)。
首先咱們來了解一下,Rebalance
目的是爲何了。
假設當前 rocketmq broker 端存在一個 topic ,擁有四個隊列,關係以下:
此時若是有一個消費者使用集羣模式消費消息,那麼它將須要負責消費全部隊列中的消息。
當咱們再增長一個消費者消費消息時,此時消費端將會自動進行重平衡,默認狀況下將會使用平均分配原則。
能夠看到 Rebalance
機制能夠提高的消息的並行處理機制。
rocketmq 消費端啓動時竟會觸發 Rebalance
機制。接着,咱們根據源碼主要看下 Rebalance
主流程,代碼位於RebalanceImpl#rebalanceByTopic
。
一般咱們使用集羣消費模式,因此這裏主要看集羣模式下 Rebalance
過程。
上述代碼總體流程以下:
AllocateMessageQueueAveragely
,即平均分配原則ProcessQueue Table
,若是有更新將會把新的隊列在加入異步消費流程。後續消息流程就不看源碼,比較複雜,網上找了一張消息消費流程圖:
能夠看到,因爲網絡端口問題,沒法正常獲取全部消費者 ID 集合,這就致使沒法正常分配隊列信息。
List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);
因爲未被分配任一隊列,消費端程序也就業務沒法正常拉取消息。
rocketmq 控制檯從新發送消息代碼以下:
MessageService
將會把消息的元數據封裝一個CONSUME_MESSAGE_DIRECTLY
類型的請求,接着調用 rocketmq 提供的 admin API,給 rocketmq broker 發送請求。
broker 端收到請求以後,將會查詢消息,而後再向消費端發起 CONSUME_MESSAGE_DIRECTLY
請求。消費端接受到消息請求以後,將會直接消息這條消息。
rocketmq broker 雖然啓動了兩個端口,可是從 rocketmq broker 的源碼能夠發現這兩個端口啓動以後起到做用是同樣的。
那爲何開啓兩個監聽端口那?我想不少同窗應該也有這個疑惑,這裏給出一個開發者解釋答案。
https://github.com/apache/roc...
普通的端口將會承載全部消息網絡請求,若是此時請求很是繁忙,broker 端的全部 I/O 線程可能都在執行請求,這就會致使後續網絡請求進入隊列,從而致使消息請求執行緩慢。
這對於生產者來講,多是一個致命的問題,由於消息生產者一般消息發送延時要低。
這種狀況下,咱們就能夠將消息發送到 VIP 端口,從而下降消息發送的延時。
默認狀況下,rocketmq 客戶端的 vipChannel
配置爲 true
。
private boolean vipChannelEnabled = Boolean.parseBoolean(System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true"));
生產者的發送消息,消費者獲取元數據信息等請求默認將會使用 vip
端口。
不過這裏須要注意一點,消費者拉取消息,將不會使用vip
端口。
雖然這個設計很巧妙,可是說實話我的以爲這個配置權限應該交給開發者本身去配置,而不是默認開啓。
由於不熟悉的狀況下仍是很容易踩坑的,默認狀況下,你們應該只熟悉 9876 與 10911 這兩個端口。
rocketmq 4.5.1 版本以後,vipChannel
配置被修改成 false
,這時是否使用 vip 端口真正交給開發者本身
若是此時想開啓,須要主動 API 參數,或者 JVM 參數增長 -Dcom.rocketmq.sendMessageWithVIPChannel=true
今天的問題主要因爲 VIP 端口沒法鏈接,從而致使消費端沒法正常消費消息。雖然最後的解決辦法很是簡單,可是這個排查過程真的很難。
咱們日常在使用 rocketmq 過程當中,一般只要設置 nameserver 的配置便可, broker 等地址信息將會自動從 nameserver 獲取。這就間接致使了,咱們可能只瞭解 9876 這個端口。
生產環境因爲網絡安全問題,通常不會開放所有的端口。因此,咱們在使用 rocketmq 的過程,須要瞭解如下四個端口,分別爲(默認配置):
生產使用 rocketmq 過程,若是碰到詭問題,不妨嘗試 telnet 看下網關連通性。另外還能夠經過查看 rocketmq 自身日誌,肯定問題,日誌位置位於:
${user.home}/logs/rocketmqlogs
好了,今天文章就到這裏。我是樓下小黑哥,你知道的越多,你不知道的就越多。
下週見~
歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客: studyidea.cn