MQ常見問題
- 消息丟失
- 消息會在哪些環節丟失
- 應對機制
- 消息的順序性
- 消息冪等
- 消息積壓的處理
消息丟失
消息會在哪些環節丟失
- 網絡傳輸環節:生產者發送消息到broker,broker中master同步消息給slave,consumer消費消息,這3個環節都是跨網絡傳輸,可能造成消息丟失。
- 緩存信息刷盤環節:MQ存盤時都會先寫?操作系統的緩存pagecache中,然后再由操作系統異步的將消息寫?硬盤。這個中間有個時間差,就可能會造成消息丟失。如果服務掛了,緩存中還沒有來得及寫?硬盤的消息就會丟失。
應對機制
- 生產者發送消息環節:消息確認機制。使用同步發送機制,可以收到broker端的發送成功確認。該方法消息安全但是效率低。
- 消息同步環節:普通master-slave集群下,master掛掉后slave不會主動切換未master,等master再次啟動后,消息不會丟失,但這種方式犧牲了可用性。高可用Dledger集群,消息要寫入半數以上節點才會通知客戶端寫成功,消息安全性比較高,可以認為不會丟失消息。但是在腦裂情況下,也有可能會丟失消息。
- 刷盤環節:RocketMQ的Broker提供了?個很明確的配置項flushDiskType,可以選擇刷盤模式。有兩個可選項,SYNC_FLUSH同步刷盤和ASYNC_FLUSH異步刷盤。所謂同步刷盤,是指broker每往?志?件中寫??條消息,就調??次刷盤操作,其實也是以10毫秒的間隔去調?刷盤操作。?異步刷盤,則是指broker每隔?個固定的時間,才去調??次刷盤操作。異步刷盤性能更穩定,但是會有丟消息的可能。?同步刷盤的消息安全性就更?,但是操作系統的IO壓?就會?常?。從理論上來說,也還是會有?正常斷電造成消息丟失的可能,甚?嚴格意義上來說,任何應?程序都不可能完全保證斷電消息不丟失。
- 消費環節:消費狀態確認機制。也就是消費者處理完消息后,需要給Broker?個響應,
表示消息被正常處理了。如果Broker端沒有拿到這個響應,不管是因為Consumer沒有拿到消息,還是Consumer處理完消息后沒有給出相應,Broker都會認為消息沒有處理成功。之后,Broker就會向Consumer重復投遞這些沒有處理成功的消息。RocketMQ是把消費失敗的消息方式重試隊列里面重新推送。如果Consumer給Broker返回了消費成功,用異步的方式去處理消息,這種情況可能會丟失消息。 - 如果MQ服務全掛掉了,想繼續保持業務運行,又想不丟失消息,通常的做法是設計?個降級緩存。Producer往MQ發消息失敗了,就往降級緩存中寫,然后,依然正常去進?后續的業務。此時,再啟動?個線程,不斷嘗試將降級緩存中的數據往MQ中發送。這樣,?少當MQ服務恢復過來后,這些消息可以盡快進?到MQ中,繼續往下游Conusmer推送,?不?于造成消息丟失。
消息的順序性
通常討論MQ的消息順序性,其實是在強調局部有序,?不是全局有序。比如某個業務流程的消息有序。
RocketMQ的順序消費機制:Producer通過設置MessageQueueSelector將?組有序的消息寫?到同?個MessageQueue中。Consumer使用Orderly的消費方式每個線程集中從?個MessageQueue中拿取消息。
消息冪等
- 消息的重復發送:Producer發送消息時,如果采?發送者確認的機制,那么Producer發送消息會等待Broker的響應。如果沒有收到Broker的響應,Producer就會發起重試。但是,Producer沒有收到Broker的響應,也有可能是Broker已經正常處理完了消息,只不過發給Producer的響應請求丟失了。這時候Producer再次發起消息重試,就有可能造成消息重復。RocketMQ的處理?式,是會在發送消息時,給每條消息分配?個唯?的ID。重試時broker可以根據msgId判斷是否已經處理。
- 消息的重復消費:RocketMQ是通過消費者的響應機制來推進offset的,如果consumer從broker上獲取了消息,正常處理之后,他要往broker返回?個響應,但是如果?絡出現波動,consumer從broker上拿取到了消息,但是等到他向broker發響應時,發??絡波動,這個響應丟失了,那么就會造成消息的重復消費。因為broker沒有收到響應,就會向這個Consumer所在的Group重復投遞消息。Comsumer端可以使用msgId進行唯一性控制,或者更嚴格的可以使?message的key屬性寫入業務的唯一屬性來控制。
消息積壓的處理
產?消息積壓的根本原因還是Consumer處理消息的效率低于消息產生的速度。所以處理消息積壓第一個可以優化Consumer處理消息的效率,第二個可以增加Consumer實例的個數。但是增加Consumer實例個數是有上限的。RocketMQ中一個Topic下的MessageQueue只能由一個消費者綁定,因此如果消費者實例數最多增加到等于MessageQueue的個數。如果此時再繼續增加Consumer的實例,那么就會有些Consumer實例是沒有MessageQueue去消費的,因此也就沒有?了。
如果要快速處理積壓的消息,可以創建?個新的Topic,配置?夠多的MessageQueue。
然后把Consumer實例的Topic轉向新的Topic,并緊急上線?組新的消費者,只負責消費舊Topic中的消息,并轉存到新的Topic中。這個速度明顯會?普通Consumer處理業務邏輯要快很多。然后在新的Topic上,就可以通過添加消費者個數來提?消費速度了。之后再根據情況考慮是否要恢復成正常情況。