貞炸了!上線以後,消息收不到了!

hello,各位小夥伴們,上午好~前端

昨晚生產系統機房切換,又度過了一個不眠之夜。趁着這段無聊時間,分享一下前一段時間 RocketMQ 踩坑經歷java

太慘了!!!早上剛躺下睡了兩小時,就被一通電話僥倖起來查看問題。

前言

事情是這樣的,前端時間咱們有個新業務上線,這個業務須要監聽支付成功的 mq 消息,而後向綁定的音箱推送消息。這樣用戶在支付完成以後,商家端就就能夠收到收款播報。git

起初咱們在測試環境的測試的時候,一切流程很是順利,沒有任何問題。可是等到咱們發佈上線以後,卻出現了問題。 github

一筆支付成功以後,音箱沒有發出收款成功的播報。一切流程排查下來以後,這才發現原來 MQ 消費端沒有正常在消費消息。apache

開始排查問題,第一想到的是消費端是否是發佈失敗了,可是查看相關日誌,並無任何異常。安全

登陸 MQ 控制檯,嘗試手動從新發布消息,神奇的事來了,消費端成功收到消息網絡

總結如今的問題,下文開始排查。異步

  1. MQ 消費端應用沒有異常,可是沒法正常消費
  2. 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 端口。

  1. 代碼主動禁止使用 vip 端口,配置以下:
## 消費端
DefaultMQPushConsumer#setVipChannelEnabled(false)
## 生產端
DefaultMQProducer#setVipChannelEnabled(false);
  1. 設置 JVM 參數,禁用 vip 端口
-Dcom.rocketmq.sendMessageWithVIPChannel=false

源碼分析

雖然問題解決了,可是上述問題本質緣由尚未找到。因此此次咱們就從源碼出發,追本溯源。

爲何 vip 端口網絡不通將會致使消費者不能正常消費?

從 rocketmq 錯誤日誌,咱們能夠看到報錯代碼位於 RebalanceService 類中。

這裏主要用來執行 topic Rebalance(重平衡)。

首先咱們來了解一下,Rebalance 目的是爲何了。

假設當前 rocketmq broker 端存在一個 topic ,擁有四個隊列,關係以下:

此時若是有一個消費者使用集羣模式消費消息,那麼它將須要負責消費全部隊列中的消息。

rocketmq 消費者-第 1 頁 的副本

當咱們再增長一個消費者消費消息時,此時消費端將會自動進行重平衡,默認狀況下將會使用平均分配原則。

能夠看到 Rebalance 機制能夠提高的消息的並行處理機制。

rocketmq 消費端啓動時竟會觸發 Rebalance 機制。接着,咱們根據源碼主要看下 Rebalance 主流程,代碼位於RebalanceImpl#rebalanceByTopic

一般咱們使用集羣消費模式,因此這裏主要看集羣模式下 Rebalance 過程。

上述代碼總體流程以下:

  1. 首先獲取 Rebalance 過程所需元數據,包括 Topic 下的隊列信息集合以及消費者組下的消費者實例 id 信息集合
  2. 二者都存在的狀況下,將會按照必定策略將隊列信息分配給每一個消費者,默認按照 AllocateMessageQueueAveragely,即平均分配原則
  3. 將預分配結果嘗試更新 ProcessQueue Table,若是有更新將會把新的隊列在加入異步消費流程。

後續消息流程就不看源碼,比較複雜,網上找了一張消息消費流程圖:

來自:https://blog.csdn.net/binzhaomobile/article/details/75004190

能夠看到,因爲網絡端口問題,沒法正常獲取全部消費者 ID 集合,這就致使沒法正常分配隊列信息。

List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);

因爲未被分配任一隊列,消費端程序也就業務沒法正常拉取消息。

爲何 mq 控制檯從新發送的消息消費者能夠收到?

rocketmq 控制檯從新發送消息代碼以下:

MessageService 將會把消息的元數據封裝一個CONSUME_MESSAGE_DIRECTLY類型的請求,接着調用 rocketmq 提供的 admin API,給 rocketmq broker 發送請求。

broker 端收到請求以後,將會查詢消息,而後再向消費端發起 CONSUME_MESSAGE_DIRECTLY 請求。消費端接受到消息請求以後,將會直接消息這條消息。

爲何 broker 將會啓動兩個端口?

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 的過程,須要瞭解如下四個端口,分別爲(默認配置):

  • 9876:nameserver 監聽端口
  • 10911: broker 監聽端口
  • 10909:broker vip 監聽端口
  • 10912:broker HA 端口,用於主從同步

生產使用 rocketmq 過程,若是碰到詭問題,不妨嘗試 telnet 看下網關連通性。另外還能夠經過查看 rocketmq 自身日誌,肯定問題,日誌位置位於:

${user.home}/logs/rocketmqlogs

好了,今天文章就到這裏。我是樓下小黑哥,你知道的越多,你不知道的就越多。

下週見~

歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客: studyidea.cn

相關文章
相關標籤/搜索