推薦閱讀:https://juejin.im/post/5d996285f265da5bad40523c
這個文章從源碼上分析了原因,並給出了解決方案。
文章解決方案一:在broker.config中將transientStorePoolEnable=true。
文章解決方案二:擴容broker服務器。
具體解決方案說明,可自行閱讀文章。
------------------------以下為原內容--------------------------------------
之前寫的解決方案,都是基於測試環境測試的.到生產環境之后,正常使用沒有問題,生產環境壓測時,又出現了system busy異常(簡直崩潰).最后在rocketmq群里大佬指導下,終於解決(希望是徹底解決).
下面直接給出結果:
目前通過生產環境各種參數修改測試得出:
broker busy異常: 可通過增大 waitTimeMillsInSendQueue 解決
system busy異常:可通過增大 osPageCacheBusyTimeOutMills 解決
#發送隊列等待時間 waitTimeMillsInSendQueue=3000 #系統頁面緩存繁忙超時時間(翻譯),默認值 1000 osPageCacheBusyTimeOutMills=5000
個人猜測,出現異常的原因是因為我們同一台服務器部署的多個應用造成的.我們一台服務器上部署了 三個ES、八個redis、一個rocketmq ,壓力測試時這些都在使用,雖然cpu、內存都還有很大剩余,但是磁盤io和內存頻率畢竟只有那么多可能已經占滿,或者還有其他都會有影響。
之前測試環境測試其他東西時,發現mq和redis同時大量使用時,redis速度會降低三到四倍,由此可見應用分服務器部署的重要性。以前知道會有影響,沒想到影響這么大。
最終結解決方案:應該給rocketmq單獨部署性能較高的服務器.
下面給下我們完整的配置:
#broker名字,注意此處不同的配置文件填寫的不一樣 brokerClusterName=rocketmqcluster brokerName=broker-a #0 表示 Master, >0 表示 Slave brokerId=0 #nameServer地址,分號分割 namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876 #這個配置可解決雙網卡,發送消息走外網的問題,這里配上內網ip就可以了 brokerIP1=10.30.51.149 #在發送消息時,自動創建服務器不存在的topic,默認創建的隊列數 defaultTopicQueueNums=8 #是否允許 Broker 自動創建Topic,建議線下開啟,線上關閉 autoCreateTopicEnable=false #是否允許 Broker 自動創建訂閱組,建議線下開啟,線上關閉 autoCreateSubscriptionGroup=true #Broker 對外服務的監聽端口 listenPort=10911 #刪除文件時間點,默認凌晨 0點 deleteWhen=03 #文件保留時間,默認 48 小時 fileReservedTime=48 #commitLog每個文件的大小默認1G mapedFileSizeCommitLog=1073741824 #ConsumeQueue每個文件默認存30W條,根據業務情況調整 mapedFileSizeConsumeQueue=1000000 destroyMapedFileIntervalForcibly=120000 redeleteHangedFileInterval=120000 #檢測物理文件磁盤空間 diskMaxUsedSpaceRatio=88 #存儲路徑 storePathRootDir=/app/data/rocketmq/data #commitLog 存儲路徑 storePathCommitLog=/app/data/rocketmq/data/commitlog #消費隊列存儲路徑存儲路徑 storePathConsumeQueue=/app/data/rocketmq/data/consumerqueue #消息索引存儲路徑 storePathIndex=/app/data/rocketmq/data/index #checkpoint 文件存儲路徑 storeCheckpoint=/app/data/rocketmq/data/checkpoint #abort 文件存儲路徑 abortFile=/app/data/rocketmq/data/abort #限制的消息大小 修改為16M maxMessageSize=16777216 #發送隊列等待時間 waitTimeMillsInSendQueue=3000 osPageCacheBusyTimeOutMills=5000 flushCommitLogLeastPages=12 flushConsumeQueueLeastPages=6 flushCommitLogThoroughInterval=30000 flushConsumeQueueThoroughInterval=180000 #Broker 的角色 #- ASYNC_MASTER 異步復制Master #- SYNC_MASTER 同步雙寫Master #- SLAVE brokerRole=ASYNC_MASTER #刷盤方式 #- ASYNC_FLUSH 異步刷盤 #- SYNC_FLUSH 同步刷盤 flushDiskType=ASYNC_FLUSH #checkTransactionMessageEnable=false #發消息線程池數量 sendMessageThreadPoolNums=80 #拉消息線程池數量 pullMessageThreadPoolNums=128 useReentrantLockWhenPutMessage=true
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 上面是最新更新 2019-06-13 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
記一次 rocketmq 使用時的異常。
這里就不說什么rocketmq 源碼啥的了,因為沒看過。網上一搜這兩個異常 大部分都是什么源碼解讀,也沒說出現后的解決辦法(藍瘦香菇)。
大量測試發現:
1、system busy , start flow control for a while
該異常會造成 消息丟失。
2、broker busy , start flow control for a while
該異常不會造成消息丟失。(這是最坑的,都異常了消息竟然是正常發送了的。)
解決過程:
1、最開始時候 ,測試發現在性能好的服務器上 只會出現system busy,也就是說出現異常就會消息丟失。
所以:業務代碼進行處理,出現異常就會重發到當前topic的 bak隊列,當時想的是既然這個topic busy了,就換到另外的topic去發,總不能都 busy吧。
也算是臨時解決了。
2、運行一年后,可能是服務器上運行的東西多了,或者其他原因。發現有消息重復的現象。不用想肯定是報broker busy異常,重發到topic的 bak隊列了。又因為broker busy可能不會造成消息丟失,所以消息重復就出現了。
3、無奈,找新的解決方法。本來想的是判斷異常,如果是broker busy就不重發了。
報着試一試的態度,又去百度了一下,還是搜出來一堆源碼解讀。搭上梯子,google一下,還真找到了。
https://stackoverflow.com/questions/47749906/rocketmq-throw-exception-timeout-clean-queuebroker-busy-start-flow-control-f
https://www.cnblogs.com/cs99lzzs/p/9181555.html
想到不知道在哪看的的一句話,在stackoverflow上能找到和你一樣的問題,那問題已經解決了百分之90了。這他喵的真實至理名言啊。
==============吐槽完=================
又經過大量測試驗證:
解決方案:
修改rocketmq配置文件:
方案一:sendMessageThreadPoolNums 改成 1 ,沒有的話新增一行。
sendMessageThreadPoolNums=1
方案二(推薦):useReentrantLockWhenPutMessage改成true,沒有的話新增一行。
sendMessageThreadPoolNums=32
useReentrantLockWhenPutMessage=true
說明:
sendMessageThreadPoolNums這個屬性是發送線程池大小, rocketmq4.1版本之后默認為 1,之前版本默認什么不知道但是肯定大於1。這個屬性改成1的話,就不用管useReentrantLockWhenPutMessage這個屬性了;
如果改成大於1,就需要將useReentrantLockWhenPutMessage這個屬性設置為 true;
目前測試 未發現這兩個方案有什么區別,sendMessageThreadPoolNums=1 時也支持多線程發送,發送速度感覺和 sendMessageThreadPoolNums大於1沒有區別,都能跑滿100M的網卡。
感覺如果useReentrantLockWhenPutMessage=true的時候,就是打開鎖(屬性名翻譯一下也大概是這個意思),然后關鍵代碼其實還是單線程處理;
有閑功夫的話去翻翻源碼看看去。
最后 我是選擇的方案二,畢竟看着好看點。