據我瞭解,在使用消息隊列遇到的問題中,消息積壓這個問題,應該是最常遇到的問題了,而且,這個問題 還不太好解決。數據庫
咱們都知道,消息積壓的直接緣由,必定是系統中的某個部分出現了性能問題,來不及處理上游發送的消 息,纔會致使消息積壓。性能優化
因此,咱們先來分析下,在使用消息隊列時,如何來優化代碼的性能,避免出現消息積壓。而後再來看看, 若是你的線上系統出現了消息積壓,該如何進行緊急處理,最大程度地避免消息積壓對業務的影響。服務器
優化性能來避免消息積壓網絡
在使用消息隊列的系統中,對於性能的優化,主要體如今生產者和消費者這一收一發兩部分的業務邏輯中。 對於消息隊列自己的性能,你做爲使用者,不須要太關注。爲何這麼說呢?多線程
主要緣由是,對於絕大多數使用消息隊列的業務來講,消息隊列自己的處理能力要遠大於業務系統的處理能 力。主流消息隊列的單個節點,消息收發的性能能夠達到每秒鐘處理幾萬至幾十萬條消息的水平,還能夠通 過水平擴展Broker的實例數成倍地提高處理能力。併發
而通常的業務系統須要處理的業務邏輯遠比消息隊列要複雜,單個節點每秒鐘能夠處理幾百到幾千次請求, 已經能夠算是性能很是好的了。因此,對於消息隊列的性能優化,咱們更關注的是,在消息的收發兩端,我 們的業務代碼怎麼和消息隊列配合,達到一個最佳的性能。框架
1. 發送端性能優化微服務
發送端業務代碼的處理性能,實際上和消息隊列的關係不大,由於通常發送端都是先執行本身的業務邏輯, 最後再發送消息。若是說,你的代碼發送消息的性能上不去,你須要優先檢查一下,是否是發消息以前的業 務邏輯耗時太多致使的。性能
對於發送消息的業務邏輯,只須要注意設置合適的併發和批量大小,就能夠達到很好的發送性能。爲何這 麼說呢?優化
咱們以前的課程中講過Producer發送消息的過程,Producer發消息給Broker,Broker收到消息後返回確認 響應,這是一次完整的交互。假設這一次交互的平均時延是1ms,咱們把這1ms的時間分解開,它包括了下 面這些步驟的耗時:
若是是單線程發送,每次只發送1條消息,那麼每秒只能發送 1000ms / 1ms * 1條/ms = 1000條 消息,這種 狀況下並不能發揮出消息隊列的所有實力。
若是你的系統是一個離線分析系統,離線系統在性能上的需求是什麼呢?它不關心時延,更注重整個系統的 吞吐量。發送端的數據都是來自於數據庫,這種狀況就更適合批量發送,你能夠批量從數據庫讀取數據,然 後批量來發送消息,一樣用少許的併發就能夠得到很是高的吞吐量。
2. 消費端性能優化
使用消息隊列的時候,大部分的性能問題都出如今消費端,若是消費的速度跟不上發送端生產消息的速度, 就會形成消息積壓。若是這種性能倒掛的問題只是暫時的,那問題不大,只要消費端的性能恢復以後,超過 發送端的性能,那積壓的消息是能夠逐漸被消化掉的。
要是消費速度一直比生產速度慢,時間⻓了,整個系統就會出現問題,要麼,消息隊列的存儲被填滿沒法提 供服務,要麼消息丟失,這對於整個系統來講都是嚴重故障。
因此,咱們在設計系統的時候,必定要保證消費端的消費性能要高於生產端的發送性能,這樣的系統才能健 康的持續運行。
消費端的性能優化除了優化消費業務邏輯之外,也能夠經過水平擴容,增長消費端的併發數來提高整體的消 費性能。特別須要注意的一點是,在擴容Consumer的實例數量的同時,必須同步擴容主題中的分區(也叫 隊列)數量,確保Consumer的實例數和分區數量是相等的。若是Consumer的實例數量超過度區數量,這 樣的擴容其實是沒有效果的。緣由咱們以前講過,由於對於消費者來講,在每一個分區上實際上只能支持單 線程消費。
我⻅到過不少消費程序,他們是這樣來解決消費慢的問題的:
它收消息處理的業務邏輯可能比較慢,也很難再優化了,爲了不消息積壓,在收到消息的OnMessage方 法中,不處理任何業務邏輯,把這個消息放到一個內存隊列裏面就返回了。而後它能夠啓動不少的業務線 程,這些業務線程裏面是真正處理消息的業務邏輯,這些線程從內存隊列裏取消息處理,這樣它就解決了單 個Consumer不能並行消費的問題。
這個方法是否是很完美地實現了併發消費?請注意,這是一個很是常⻅的錯誤方法! 爲何錯誤?由於會丟消息。若是收消息的節點發生宕機,在內存隊列中還沒來及處理的這些消息就會丟失。
消息積壓了該如何處理?
還有一種消息積壓的狀況是,平常系統正常運轉的時候,沒有積壓或者只有少許積壓很快就消費掉了,可是 某一個時刻,忽然就開始積壓消息而且積壓持續上漲。這種狀況下須要你在短期內找到消息積壓的緣由, 迅速解決問題纔不至於影響業務。
致使忽然積壓的緣由確定是多種多樣的,不一樣的系統、不一樣的狀況有不一樣的緣由,不能一律而論。可是,我 們排查消息積壓緣由,是有一些相對固定並且比較有效的方法的。
能致使積壓忽然增長,最粗粒度的緣由,只有兩種:要麼是發送變快了,要麼是消費變慢了。
大部分消息隊列都內置了監控的功能,只要經過監控數據,很容易肯定是哪一種緣由。若是是單位時間發送的 消息增多,好比說是遇上大促或者搶購,短期內不太可能優化消費端的代碼來提高消費性能,惟一的方法 是經過擴容消費端的實例數來提高整體的消費能力。
若是短期內沒有足夠的服務器資源進行擴容,沒辦法的辦法是,將系統降級,經過關閉一些不重要的業 務,減小發送方發送的數據量,最低限度讓系統還能正常運轉,服務一些重要業務。
還有一種不太常⻅的狀況,你經過監控發現,不管是發送消息的速度仍是消費消息的速度和原來都沒什麼變 化,這時候你須要檢查一下你的消費端,是否是消費失敗致使的一條消息反覆消費這種狀況比較多,這種情 況也會拖慢整個系統的消費速度。
若是監控到消費變慢了,你須要檢查你的消費實例,分析一下是什麼緣由致使消費變慢。優先檢查一下日誌 是否有大量的消費錯誤,若是沒有錯誤的話,能夠經過打印堆棧信息,看一下你的消費線程是否是卡在什麼 地方不動了,好比觸發了死鎖或者卡在等待某些資源上了。
小結
這節課咱們主要討論了2個問題,一個是如何在消息隊列的收發兩端優化系統性能,提早預防消息積壓。另 外一個問題是,當系統發生消息積壓了以後,該如何處理。
優化消息收發性能,預防消息積壓的方法有兩種,增長批量或者是增長併發,在發送端這兩種方法均可以使 用,在消費端須要注意的是,增長併發須要同步擴容分區數量,不然是起不到效果的。
對於系統發生消息積壓的狀況,須要先解決積壓,再分析緣由,畢竟保證系統的可用性是首先要解決的問 題。快速解決積壓的方法就是經過水平擴容增長Consumer的實例數量。