概念說明
通常一個訊息佇列需要掌握的知識點有Topic(主體)、Producer(生產者)、Consumer(消費者)、Queue(佇列)、Delivery Semantics(訊息傳遞正規化)
蛋疼的是不同的訊息佇列關於這些名詞叫法不一樣,含義也不是很精確。所以阿里起了一個專案OpenMessaging去發起首個分散式訊息領域的國際標準。不過好像並沒有多少人買賬,但這並不妨礙我們按照這個規範去梳理學習訊息佇列的知識。
有興趣的可以對照著看:https://github.com/openmessaging/specification/blob/master/specification-schema.md
rocketmq官方文件已經說的比較清楚了,不再贅述
核心流程
訊息寫入與儲存
訊息是儲存到broker中的,寫到commit log中,先寫記憶體,在刷盤。
儲存到磁碟中是直接以檔案系統的方式。為了提高磁碟寫入效率,都是順序寫,這樣所有的topic都放在了一起,這一點與kafka不同,kafka以topic作為基本單元。
單個commitlog 檔案大小為1G,之後滾動寫入不同檔案。
訊息讀取
消費者先從ConsumeQueue(訊息邏輯佇列)讀取持久化訊息的offset(偏移量)、size(大小)和訊息Tag的HashCode值,再從CommitLog中讀取訊息的真正實體內容部分;
此外為了快速定位訊息,還有一種檔案叫index,在給定訊息 Topic 和 Key 的前提下,可以快速定位訊息
注意點
使用訊息佇列時,需要注意的地方
訊息儲存時間
rocketmq預設儲存72小時,超過了,無論有沒有消費都會丟棄,通過引數fileReserverdTime
來配置。
注意這個配置是全域性配置,沒法針對不同的topic設定不同的值,原因在上面已經提到過了,因為rocketmq儲存訊息是所有的topic放在一起的。
訊息有序
MessageListenerOrderly
訊息丟失
理論上可以保證不丟失(接受訊息重複,以及一定程度的寫入效能下降),
生產端
同步模式,或者非同步模式時需要處理髮送失敗情況
所以保證消費的冪等性是必須的
broker端
為了保證不丟,需要開啟同步刷盤(防止記憶體資料丟失),同步複製(防止單點故障)。
這樣是有效能損失的。刷盤機制引數flushDiskType
預設是ASYNC_FLUSH
,broker 會訊息一定量後再刷盤,顯然效能更好。
消費端
消費完再CONSUME_SUCCESS
生產端,消費端都有可能因為網路問題導致訊息成功了,但是ack沒有成功,所以會重複投遞/消費。所以Delivery Semantics
一般選擇At least once
。應用程式必須要保證消費的冪等性
寫入效率/消費效率/消費積壓
傳送端
非同步刷盤,非同步複製情況下,兩臺4核8G,大小100Byte,寫入速度能夠達到幾萬。
通常broker端不存在瓶頸。但是由於一般業務是是共用一個叢集的,各個業務線都使用起來,流量還是很高的,需要監控報警,及時進行水平擴容。
如果能夠接受延遲,producer可以批量提交,傳送效率更高。
消費端
取決於消費邏輯是否耗時,預設單機處理執行緒consumeThreadMax
(預設20)如果消費端服務時獨立的,可以調整調整更大,提高單機處理速度。
無法提高單機處理速度的時候,可以叢集水平擴充套件。不過不是無限水平擴充套件的,超過defaultTopicQueueNums
訂閱佇列數無效,該值預設值為4
應用場景
為什麼需要訊息佇列,這個問題都被講爛了,經典三大場景還是削峰填谷、非同步處理、服務解耦。
個人覺得這邊總結的比較全面。https://github.com/openmessaging/specification/blob/master/usecase.md
重點介紹下rpc
場景,注意這個rpc不是rpc呼叫。是同步訊息,相當於兩次rpc。client發到server。server處理完再發到client。
這個通常用於服務間的同步處理。比如有個核心服務A,某個請求裡面需要有個高耗時的操作,為了不影響A服務,用了一個B服務來處理這個操作。這時候就會用到rpc
參考
https://tech.meituan.com/2016/07/01/mq-design.html
http://tinylcy.me/2019/the-design-of-rocketmq-message-storage-system/