本文將在 RocketMQ 消息發送system busy、broker busy緣由分析與解決方案 的基礎上,結合生產上的日誌嘗試再次理解 broker busy 以及探討解決方案。java
首先,broker busy 相關的日誌關鍵字以下:服務器
上述前面4個關鍵字在上篇文章中已詳細介紹,本文先對出現上述錯誤進行一個總結,具體的分析過程請查閱上篇文章。併發
本文先給出一張流程圖,展現上述5種 broker busy 分別會在消息發送的哪一個階段拋出,以便你們可以清晰的瞭解其發生的緣由。 性能
針對前4種 broker busy 出現的問題已經在上篇文章中詳細介紹,主要是因爲 Broker 在追加消息時持有的鎖時間超過了設置的1s,Broker 爲了自我保護,會拋出錯誤,客戶端會選擇其餘 broker 服務器進行重試。若是對不是金融級服務,建議將 transientStorePoolEnable = true,能夠有效避免前面 4 種 broker ,由於開啓這個參數,消息首先會存儲在堆外內存中,而且 RocketMQ 提供了內存鎖定的功能,其追加性能能獲得必定的保障,這樣能夠作到在內存使用層面的讀寫分離,即寫消息是直接寫入堆外內存,消費消息直接從 pagecache中讀,而後定時將堆外內存的消息寫入 pagecache。但這種方案隨之帶來的就是可能存在消息丟失,若是對消息很是嚴謹的話,建議擴容集羣,或遷移topic到新的集羣。this
同時在作 Broker 服務器巡檢的時候,能夠經過去經過以下命令去查看 broker 一次消息追加是否會超過 500 ms。 .net
在這個圖中咱們看到在設置了 transientStorePoolEnable 爲 true 的狀況下,雖然一天只有一條超過500ms的消息,但也值得警戒了,因爲對系統內核參數掌握程度不夠,這種狀況,估計只能走集羣擴容的路子了。但若是一天消息量巨大並且出現頻率不高的狀況,因爲有重試機制,倒不會帶來太大的問題。若是出現太多的錯誤,建議集羣擴容。3d
本文接下來想重點探討一下 [TIMEOUT_CLEAN_QUEUE]broker busy 這種狀況。日誌
BrokerFastFailure#cleanExpiredRequestcode
while (true) { try { if (!this.brokerController.getSendThreadPoolQueue().isEmpty()) { final Runnable runnable = this.brokerController.getSendThreadPoolQueue().peek(); if (null == runnable) { break; } final RequestTask rt = castRunnable(runnable); if (rt == null || rt.isStopRun()) { break; } final long behind = System.currentTimeMillis() - rt.getCreateTimestamp(); if (behind >= this.brokerController.getBrokerConfig().getWaitTimeMillsInSendQueue()) { if (this.brokerController.getSendThreadPoolQueue().remove(runnable)) { rt.setStopRun(true); rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format("[TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d", behind, this.brokerController.getSendThreadPoolQueue().size())); } } else { break; } } else { break; } } catch (Throwable ignored) { } }
能夠看出來,拋出這種錯誤,在 broker 尚未發送「嚴重」的 pagecache 繁忙,即消息追加到內存中的最大時延沒有超過 1s,一般追加是很快的,絕大部分都會低於1ms,但可能會因爲出現一個超過200ms的追加時間,致使排隊中的任務等待時間超過了200ms,則此時會觸發broker 端的快速失敗,讓請求快速失敗,便於客戶端快速重試。可是這種請求並非實時的,而是每隔10s 檢查一遍。orm
值得注意的是,一旦出現 TIMEOUT_CLEAN_QUEUE,可能在一個點會有多個這樣的錯誤信息,具體多少與當前積壓在待發送隊列中的個數有關。
關於 [TIMEOUT_CLEAN_QUEUE]broker busy 咱們也能夠適當調整 waitTimeMillsInSendQueue,默認值爲200ms,能夠適當調整到400ms。
若是你們以爲文章對您有幫助的話,麻煩幫忙點個贊,謝謝。
> 做者介紹:《RocketMQ技術內幕》做者,維護公衆號:中間件興趣圈,目前主要發表了源碼閱讀java集合、JUC(java併發包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列源碼。