Topic太多!RocketMQ炸了!
網上部落格常說,kafka的topic數量過多會影響kafka,而RocketMQ不會受到topic數量影響。
但是,果真如此嗎?
最近排查一個問題,發現RocketMQ穩定性同樣受到topic數量影響!!
好了,一起來回顧下這次問題排查吧,最佳實踐和引申思考放在最後,千萬不要錯過。
1、問題描述
我們的RocketMQ叢集為4.6.0版本,按照3個nameserver,2個broker,每個broker為主從雙節點部署。
某天收到警報,broker-b突然從nameserver掉線,且主從雙節點都無法重新註冊。
2、初步排查
2.1 檢查程式存活&網路
因為控制檯上顯示broker-a正常,因此可以認為 nameserver、broker-a都是正常的,問題出在broker-b上。
當時第一反應是broker-b程式掛了,或者網路不通了。
登陸broker節點,看到程式依然存活。
然後透過telnet檢查和nameserver的聯通性,顯示正常,網路沒有問題。
2.2 檢查日誌
檢查broker日誌,馬上發現了異常。
2023-01-09 14:07:37 WARN brokerOutApi_thread_3 - registerBroker Exception, mqnameserver3:xxxx
org.apache.rocketmq.remoting.exception.RemotingSendRequestException: send request to <qnameserver3/xx.xx.xx.xxx:xxxx> failed
at org.apache.rocketmq.remoting.netty.NettyRemotingAbstract.invokeSyncImpl(NettyRemotingAbstract.java:429) ~[rocketmq-remoting-4.6.0.jar:4.6.0]
at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:373)
......
異常比較明確,broker請求nameserver失敗,所以導致無法註冊到叢集中。
那為什麼會註冊失敗呢?沒有非常明確的提示,因此去看下nameserver上的日誌資訊。
2023-01-09 14:09:26 ERROR NettyServerCodecThread_1 - decode exception, xx.xxx.xx.xxx:40093
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 16777216: 16777295 - discarded
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:499) [netty-all-4.0.42.Final.jar:4.0.42.Final]
......
這個異常看起來是nameserver上的netty丟擲的,請求過大丟擲了異常。
根據日誌關鍵字,直接定位到了原始碼,確實有預設的大小限制,並且可以透過com.rocketmq.remoting.frameMaxLength
進行控制。
2.3 原始碼分析
雖然找到了異常的直接原因,但是為什麼broker突然會有這麼大的請求?是什麼帶來的?
從broker的warning日誌中,並沒有辦法看到更多有效資訊。
因此,還是得深入分析下broker上的原始碼。根據日誌關鍵字,很快找到broker中的異常位置
注意!這裡透過遍歷nameserverlist,線上程池中非同步註冊,跟後面的一個小知識點有關。
從原始碼中可以分析出,如果有過大的請求的話,應該就是這個requestBody引起,它攜帶了大量topic資訊topicConfigWrapper
。
但是我們在控制檯上看到當前叢集中,只有300+topic(這裡其實是一個誤區,最後會解釋),理論上來說是非常小的,為什麼會超出容量限制呢?
看了下原始碼上下文,並沒有對reqeustBody或者topicConfigWrapper
有相關日誌的記錄,因此,還是需要arthas來看看了。
2.4 arthas定位
直接透過arthas定位實際記憶體值
watch org.apache.rocketmq.broker.out.BrokerOuterAPI registerBrokerAll {params,returnObj} -x 3
檢視結果
啥玩意?!
topicConfigTable
的map
大小為size=71111
?!!
進一步看看這些topic裡面都是些啥?我們調整下arthas的引數-x為4,改變watch變數的深度。
發現問題了!
我們看到了大量%RETRY%開頭的topic。
3、根本原因
至此,根本原因就能明確了。
RETRY topic過多,導致 broker 向 nameserver 傳送心跳(定時傳送註冊請求)時,心跳請求中攜帶的 body 上的 topic 資訊過大,超過了 nameserver 上使用的 NettyDecoder.java
限制的 16M (預設值),心跳請求失敗,所以broker掉線。
4、恢復
既然問題基本確定了,那麼先嚐試恢復吧。
前面已經看到了對最大請求體的配置,因此,我們在bin/runserver.sh
中新增一個JAVA_OPTION
對com.rocketmq.remoting.frameMaxLength
進行配置。然後重啟nameserver。
重新觀察broker,果然重啟成功了。
2023-01-09 16:03:55 INFO brokerOutApi_thread_3 - register broker[0]to name server mqnameserver4:9876 OK
2023-01-09 16:03:55 INFO brokerOutApi_thread_4 - register broker[0]to name server mqnameserver2:9876 OK
當然,這只是臨時恢復措施,後面重點要思考以下問題並進行最佳化:
RETRY topic數量這麼多是否正常?是否可以清理無效topic?
如何做好後續的topic數量監控告警?
5、最佳實踐
5.1 定時刪除無效RETRY topic
考慮使用定時任務掃描所有業務topic下的消費組,再根據消費組狀態(狀態為not_online的消費組),拼出對應RETRY topic進行刪除。以上步驟均有開源MQ sdk 的 api 可以呼叫。
即使後續消費組重新使用,RETRY topic 也會重新建立,不影響消費。
5.2 topic總數監控
前面說到在控制檯上看到當前叢集中只有300+topic,這裡其實是一個誤區,只勾選了NORMAL型別的topic,並沒有注意RETRY、DLQ、SYSTEM型別的topic。
而這次幾萬個topic基本都是RETRY型別的。
後續需要新增topic數量監控(包括RETRY型別),防止由於topic數量過多,導致broker註冊失敗。
6、引申思考
6.1 RETRY topic是什麼?為什麼有這麼多?
這需要從RocketMQ的重試機制與死信機制說起。
RocketMQ 提供了自帶的重試機制,訊息消費失敗或超時,會被投遞到 RETRY topic。RETRY topic 裡的訊息會按照延時佇列的延時時間進行消費,這樣也避免了有問題的訊息阻塞正常消費。
RETRY topic 裡儲存的是消費狀態為 consumer_later 的訊息,在重試達到 16 次(預設值)以後,訊息會進入死信佇列(本質上也是一個新的topic型別,DLS topic)。
DLQ topic在使用時才會建立,因此不會像RETRY topic 這樣大量膨脹。
但是,RETRY topic不一樣。它是由RocketMQ服務端自動建立,建立的時機有兩個:
消費失敗的時候,將訊息傳送回 broker,這時候會在服務端建立RETRY topic
consumer client 和服務端保持心跳時建立RETRY topic
線下環境的消費組存在大量的臨時測試group,而 RocketMQ會給每個實際存在的消費組建立RETRY topic,導致 RETRY topic 大量膨脹。
6.2 如果所有訊息自動重試,順序訊息會亂序嗎?
我們知道,RocketMQ中包含三種訊息型別:普通訊息、普通有序訊息、嚴格有序訊息。
三種訊息的型別介紹如下:
普通訊息:訊息是無序的,任意傳送傳送哪一個佇列都可以。
普通有序訊息:同一類訊息(例如某個使用者的訊息)總是傳送到同一個佇列,在異常情況下,也可以傳送到其他佇列。
嚴格有序訊息:訊息必須被髮送到同一個佇列,即使在異常情況下,也不允許傳送到其他佇列。
對於這三種型別的訊息,RocketMQ對應的提供了對應的方法來分別訊息:
//傳送普通訊息,異常時預設重試
public SendResult send(Message msg)
//傳送普通有序訊息,透過selector動態決定傳送哪個佇列,異常預設不重試,可以使用者自己重試,併傳送到其他佇列
public SendResult send(Message msg, MessageQueueSelector selector, Object arg)
//傳送嚴格有序訊息,透過指定佇列,保證嚴格有序,異常預設不重試
public SendResult send(Message msg, MessageQueue mq)
所以RocketMQ客戶端的生產者預設重試機制,只對普通訊息有作用。對於普通有序訊息、嚴格有序訊息是沒有作用。
6.3 nameserver資料一致性問題
在透過修改啟動引數com.rocketmq.remoting.frameMaxLength
進行臨時恢復的時候,發現一個問題:日誌恢復了,但是控制檯上卻仍然沒有顯示broker-b。
排查了下發現,由於nameserver有4臺,只重啟了一臺,而控制檯連線訪問的nameserver是另一臺,所以顯示不正確。
透過切換控制檯nameserver地址,就能看到broker-b了。
為什麼不同nameserver允許資料不一致呢?
前面在排查的過程中也發現了,broker原始碼中透過遍歷nameserverlist,線上程池中非同步註冊topic資訊到nameserver。
而這也體現了RocketM中對nameserver的設計思想。
nameserver是一個AP元件,而不是CP元件!
在 RocketMQ 中 Nameserver 叢集中的節點相互之間不通訊,各節點相互獨立,實現非常簡單。但同樣會帶來一個問題:
Topic 的路由資訊在各個節點上會出現不一致。
那 Nameserver 如何解決這個問題呢?RocketMQ 的設計者採取的方案是不解決,即為了保證 Nameserver 的高效能,允許存在這些缺陷。
NameServer之間不通訊,訊息傳送端透過PULL方式更新topic資訊,無法及時感知路由資訊的變化,因此引入了訊息傳送重試(只針對普通訊息)與故障規避機制來保證訊息的傳送高可用。
事實上,在RocketMQ的早期版本,即MetaQ 1.x和MetaQ 2.x階段,也是依賴Zookeeper的(CP型元件)。但MetaQ 3.x(即RocketMQ)卻去掉了ZooKeeper依賴,轉而採用自己的NameServer。
NameServer資料不一致,比較大的影響就是topic的佇列會存在負載不均衡的問題,以及消費端的重複消費問題,這些問題對訊息佇列來說都是可以忍受的,只要最終能保持一致,恢復平衡即可。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2942074/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- RocketMQ Compaction Topic的設計與實現MQ
- RocketMQ Series---No route info of this topic異常分析MQ
- 炸了!3年圖片都沒了
- Apache RocketMQ中如果一個topic堆積了非常多的訊息會導致rockemq出現什麼問題ApacheMQ
- RocketMQ同一個消費者唯一Topic多個tag踩坑經歷MQ
- nsq topic
- 2024 杭州測試沙龍 TOPIC 徵集開始了
- MySQL資料太多,經常卡死!不想幹了!MySql
- 求助各位好心人,頭快炸了
- ChatGPT 再出【王炸】,人工客服要失業了?ChatGPT
- kafka檢視topicKafka
- 炸了!一口氣問了我18個JVM問題!JVM
- 網站剛上線,就被 DDoS 攻擊炸了!網站
- kafka2.x常用命令筆記(一)建立topic,檢視topic列表、分割槽、副本詳情,刪除topic,測試topic傳送與消費Kafka筆記
- RabbitMQ - SpringBoot 案例 - topic 模式MQSpring Boot模式
- 【日記】不小心把 Bot 搞炸了(586 字)
- 貞炸了!上線之後,訊息收不到了!
- 終於弄明白了 RocketMQ 的儲存模型MQ模型
- RocketMQ Consumer 啟動時都幹了些啥?MQ
- Javaer 進階必看的 RocketMQ ,就這篇了JavaMQ
- Kafka原理剖析之「Topic建立」Kafka
- 【RabbitMQ】topic type exchange example in golangMQGolang
- 傻瓜式的第一個go程式,然後炸了Go
- 要炸了!剛寫完這段程式碼,就被開除了
- 因為一次刪帖事件,NGA被噴炸了事件
- 這 5 個簡單的面試題,把群炸了面試題
- 被監控轟炸了,不得不使出絕招
- 不經歷太多
- ogg 真的太多bug了,不穩定,太坑Action: Contact Oracle Support.Oracle
- 假如有人把支付寶儲存伺服器炸了伺服器
- 滴滴釋出2018年度總結:又有網友炸鍋了
- RabbitMQ的使用--以topic路由為例MQ路由
- RabbitMQ Go客戶端教程5——topicMQGo客戶端
- kafka-eagle刪除topic需要tokenKafka
- RocketMqMQ
- ?【Alibaba中介軟體技術系列】「RocketMQ技術專題」Broker服務端自動建立topic的原理分析和問題要點指南MQ服務端
- 程式碼Bug太多?給新人Code Review頭都大了?快來試試SpotBugsView
- RocketMQ(7)---RocketMQ順序消費MQ