RocketMQ調(diào)優(yōu)心得總結(jié)
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
一、問題
線上RocketMQ 集群,偶爾報錯如下:
(1)[REJECTREQUEST]system busy, start flow control for a while
(2)[TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: 206ms, size of queue: 5二、調(diào)優(yōu)歷程
Google資料
翻閱github 和 stackoverflow,在stackoverflow 上找到該問題:https://stackoverflow.com/questions/47749906/rocketmq-throw-exception-timeout-clean-queuebroker-busy-start-flow-control-f
按照以上方式增加配置文件參數(shù):
sendMessageThreadPoolNums=64
useReentrantLockWhenPutMessage=true 之后重啟觀察,報錯確實變少了,但量大了后,還有報錯。期間對sendMessageThreadPoolNums參數(shù)做過多次調(diào)整,32 、64、128 都試過,發(fā)現(xiàn)在 4核CPU, 30G內(nèi)存的機器上,配置sendMessageThreadPoolNums超過64 反倒表現(xiàn)的更不好。本人最后選擇sendMessageThreadPoolNums=32。具體多少合適,最好通過壓測評估。
擼源碼
此時還會報 broker busy,實在沒辦法,只能看源碼,尤其對報錯前后相關(guān)代碼多次閱讀,基本了解了報錯原因。
兩個報錯的代碼如下:

通過閱讀源碼,得出結(jié)果:mq busy是因為線程等待時間,超過了waitTimeMillsInSendQueue的值,該值默認 200ms。
所以還需分析,到底是什么原因,導致線程在等待,并超過了200ms,有兩種可能:
1. mq消息消費速度慢,消息堆積,導致消息寫入內(nèi)存變慢,超過了200ms
2. GC時JVM STW(stop the world) ,導致超時
之后對 JVM 進行了調(diào)優(yōu),選用 G1回收器,并且觀察 gc 時間,full GC 基本沒有了,新生代gc 時間控制在50ms 之內(nèi),而且頻率不高。
JVM 優(yōu)化后,還是偶爾有 broker busy.
配置優(yōu)化
查看rocketmq 日志,存儲層日志store.log:

可以看出,在消息發(fā)送時,主從同步時,連接從服務(wù)器等待超時,自動斷了連接。
如果是同步雙寫模式,master會等待slave也寫成功才會給客戶端返回成功。這個也會耗時,性能不好,建議使用異步。
優(yōu)化建議:主從同步使用異步,配置brokerRole=ASYNC_MASTER。
到此,優(yōu)化配置:
#主從同步模式
brokerRole=ASYNC_MASTER
#消息發(fā)送隊列等待時間,默認200
waitTimeMillsInSendQueue=400
#發(fā)送消息的最大線程數(shù),默認1,因為服務(wù)器配置不同,具體多少需要壓測后取最優(yōu)值
sendMessageThreadPoolNums=32
#發(fā)送消息是否使用可重入鎖(4.1版本以上才有)
useReentrantLockWhenPutMessage=true到此,MQ 基本穩(wěn)定,連續(xù)幾個月再沒有報錯。
后來MQ集群內(nèi)存快不夠了,擴了集群,擴為之前2倍節(jié)點。
擴容后過段時間,又出現(xiàn)了 以上兩個錯, broker busy 報錯居多。。。
此時第一感覺是新增的機器問題,通過Falcon監(jiān)控,觀察分析后,發(fā)現(xiàn)報錯期間,CPU、內(nèi)存、IO 等都無異常,有部分報錯時間點 swap 波動較大。
當內(nèi)存不夠用時,rocketMQ 會使用交換區(qū),使用交換區(qū)性能較差,這里就不對swap做過多解釋了。
對Linux內(nèi)核參數(shù)調(diào)優(yōu):
查看 cat /proc/sys/vm/swappiness
設(shè)置 swappiness = 1減少使用交換區(qū),提升性能。
優(yōu)化該參數(shù)后,報錯減少。
后來上線異步消息,并發(fā)增高,隨著使用方量級增長,broker busy 還是會出現(xiàn)。
在和 system busy 和 broker busy 的斗爭中,我對RocketMQ的了解也在加深。
對于RocketMQ 集群,和其他使用者也有過交流,總之經(jīng)驗是:多個小集群 優(yōu)于 一個大集群。
調(diào)優(yōu)時,多關(guān)注磁盤IO 和 內(nèi)存使用情況及釋放時間點,重點關(guān)注以下指標:

三、最終的參數(shù)調(diào)優(yōu)方案
機器配置參考:CPU 4核 \ 內(nèi)存30G
RocketMQ 的配置
#主從異步復(fù)制
brokerRole=ASYNC_MASTER
#異步刷盤
flushDiskType=ASYNC_FLUSH
#線上關(guān)閉自動創(chuàng)建topic
autoCreateTopicEnable=false
#發(fā)送消息的最大線程數(shù),默認1
sendMessageThreadPoolNums=32
#使用可重入鎖
useReentrantLockWhenPutMessage=true
#發(fā)送消息線程等待時間,默認200ms
waitTimeMillsInSendQueue=1000
#開啟臨時存儲池
transientStorePoolEnable=true
#開啟Slave讀權(quán)限(分擔master 壓力)
slaveReadEnable=true
#關(guān)閉堆內(nèi)存數(shù)據(jù)傳輸
transferMsgByHeap=false
#開啟文件預(yù)熱
warmMapedFileEnable=trueJVM
-server -Xms10g -Xmx10g
-XX:+UseG1GC -XX:MaxGCPauseMillis=80 -XX:G1HeapRegionSize=16m
-XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30
-XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8
-verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m
-XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=15g
-XX:-UseLargePages -XX:-UseBiasedLocking -XX:+PrintGCApplicationStoppedTime
-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1
-XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCDateStamps
-XX:+PrintAdaptiveSizePolicyLinux
vm.extra_free_kbytes,告訴VM在后臺回收(kswapd)啟動的閾值與直接回收(通過分配進程)的閾值之間保留額外的可用內(nèi)存。RocketMQ使用此參數(shù)來避免內(nèi)存分配中的長延遲。(與具體內(nèi)核版本相關(guān))
vm.min_free_kbytes,如果將其設(shè)置為低于1024KB,將會巧妙的將系統(tǒng)破壞,并且系統(tǒng)在高負載下容易出現(xiàn)死鎖。
vm.max_map_count,限制一個進程可能具有的最大內(nèi)存映射區(qū)域數(shù)。RocketMQ將使用mmap加載CommitLog和ConsumeQueue,因此建議將為此參數(shù)設(shè)置較大的值。(agressiveness --> aggressiveness)
vm.swappiness,定義內(nèi)核交換內(nèi)存頁面的積極程度。較高的值會增加攻擊性,較低的值會減少交換量。建議將值設(shè)置為10來避免交換延遲。
File descriptor limits,RocketMQ需要為文件(CommitLog和ConsumeQueue)和網(wǎng)絡(luò)連接打開文件描述符。我們建議設(shè)置文件描述符的值為655350。
Disk scheduler,RocketMQ建議使用I/O截止時間調(diào)度器,它試圖為請求提供有保證的延遲。
1. 減少使用交換區(qū)
swappiness = 12. Disk scheduler使用DeadLine IO調(diào)度器
查看IO調(diào)度器:
#查看機器整體
dmesg | grep -i scheduler
#查看某個磁盤的調(diào)度器
cat /sys/block/vda/queue/scheduler修改IO調(diào)度器:
echo deadline > /sys/block/vda/queue/scheduler注意:Linux version 3.10 以上的版本,會出現(xiàn)修改不成功。
如下:

如果你的Linux 服務(wù)器,查看磁盤IO調(diào)度器發(fā)現(xiàn)是 none, none 意思是不使用 IO調(diào)度器,一般都是開啟了 blk-mq, 查看/sys/block/vda/ 下,如果有mq目錄,說明使用的是blk-mq機制。
Blk-mq
Linux 3.13引入了塊層的新框架 blk-mq(多隊列塊IO排隊機制),并且在3.16內(nèi)核中已具有完整的功能。Blk-mq 通過8插槽服務(wù)器上的高性能閃存設(shè)備(例如PCIe SSD)允許超過1500萬IOPS。
Blk-mq無縫集成到Linux存儲堆棧中。它為設(shè)備驅(qū)動程序提供了基本功能,用于將I / O查詢映射到多個隊列。任務(wù)分布在多個線程中,因此分布到多個CPU內(nèi)核(每個內(nèi)核軟件隊列)。兼容Blk-mq的驅(qū)動程序通知blk-mq設(shè)備支持多少個并行硬件隊列(作為硬件調(diào)度隊列注冊的一部分的提交隊列的數(shù)量)。基于Blk-mq的設(shè)備驅(qū)動程序會繞過以前的Linux I / O調(diào)度程序。根據(jù)Linux I / O調(diào)度程序(基于request_fn的方法,請參閱Linux I / O堆棧圖),使用前一個塊I / O層的所有設(shè)備驅(qū)動程序?qū)⒗^續(xù)獨立于blk-mq用作基于請求的驅(qū)動程序。
所以,如果你的MQ服務(wù)器硬盤是SSD 或者 內(nèi)核為 3.10以上的,理論上使用blk-mq性能將會更高,不需要修改。
Netty
相關(guān)配置參數(shù)
1. jvm參數(shù)加:-Dio.netty.recycler.maxCapacity.default=0
2. jvm參數(shù)去掉-XX:+DisableExplicitGC
Netty性能問題
1.查閱netty相關(guān)資料,在netty的github上找到了一個issue #4147,大致可以看出,netty實現(xiàn)的Recycler并不保證池的大小,也就是說有多少對象就往池中加入多少對象,從而可能撐滿服務(wù)器。通過在jvm啟動時加入-Dio.netty.recycler.maxCapacity.default=0參數(shù)來關(guān)閉Recycler池,前提:netty version>=4.0。所以可懷疑為內(nèi)存泄露!
千萬不要開啟-XX:+DisableExplicitGC!因為netty要做System.gc()操作,而System.gc()會對直接內(nèi)存進行一次標記回收,如果通過DisableExplicitGC禁用了,會導致netty產(chǎn)生的對象爆滿
2.Netty里四種主力的ByteBuf,
其中UnpooledHeapByteBuf 底下的byte[]能夠依賴JVM GC自然回收;而UnpooledDirectByteBuf底下是DirectByteBuffer,如Java堆外內(nèi)存掃盲貼所述,除了等JVM GC,最好也能主動進行回收;而PooledHeapByteBuf 和 PooledDirectByteBuf,則必須要主動將用完的byte[]/ByteBuffer放回池里,否則內(nèi)存就要爆掉。所以,Netty ByteBuf需要在JVM的GC機制之外,有自己的引用計數(shù)器和回收過程。
在DirectByteBuffer中,首先向Bits類申請額度,Bits類有一個全局的 totalCapacity變量,記錄著全部DirectByteBuffer的總大小,每次申請,都先看看是否超限 -- 堆外內(nèi)存的限額默認與堆內(nèi)內(nèi)存(由-XMX 設(shè)定)相仿,可用 -XX:MaxDirectMemorySize 重新設(shè)定。
如果已經(jīng)超限,會主動執(zhí)行Sytem.gc(),期待能主動回收一點堆外內(nèi)存。然后休眠一百毫秒,看看totalCapacity降下來沒有,如果內(nèi)存還是不足,就拋出大家最頭痛的OOM異常。
心得:
MQ 調(diào)優(yōu)過程,需要關(guān)注 disk 、mem 、IO、CPU 等方面指標,尤其要關(guān)注 iowait , 磁盤iowait 會直接影響性能。
發(fā)送消息線程數(shù)并不是越多越好,太多的線程,CPU 上下文切換也是很大的性能損耗。
需要對Linux 有較為深刻的了解,其中涉及 頁緩存、缺頁中斷、零拷貝、預(yù)讀、回寫、內(nèi)存映射、IO調(diào)度器。
其他技術(shù):并發(fā)處理、鎖機制、異步、池化技術(shù)、堆外內(nèi)存、Netty 等相關(guān)技術(shù)。
只有對以上這些技術(shù)都有深刻理解,才能很好的理解RocketMQ,并對其做出合理的優(yōu)化。
————————————————
版權(quán)聲明:本文為CSDN博主「CleverApe」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qq_27641935/article/details/106015319
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
