1. Rocketmq消費模型(實時性)
常見的數據同步方式有這幾種:
push:producer發送消息后,broker馬上把消息投遞給consumer。這種方式好在實時性比較高,但是會增加broker的負載;而且消費端能力不同,如果push推送過快,消費端會出現很多問題。
pull:producer發送消息后,broker什么也不做,等着consumer自己來讀取。它的優點在於主動權在消費者端,可控性好;但是間隔時間不好設置,間隔太短浪費資源,間隔太長又會消費不及時。
長輪詢:當consumer過來請求時,broker會保持當前連接一段時間 默認15s,如果這段時間內有消息到達,則立刻返回給consumer;15s沒消息的話則返回空然后重新請求。這種方式的缺點就是服務端要保存consumer狀態,客戶端過多會一直占用資源。
RocketMQ默認是采用pushConsumer方式消費的,從概念上來說是推送給消費者,它的本質是pull+長輪詢。這樣既通過長輪詢達到了push的實時性,又有了pull的可控性。系統收到消息后會自動處理消息和offset(消息偏移量),如果期間有新的consumer加入會自動做負載均衡(集群模式下offset存在broker中; 廣播模式下offset存在consumer里)。當然我們也可以設置為pullConsumer模式,這樣靈活性會提高,但是代碼卻會很復雜,需要手動維護offset,消息存儲和狀態。
* offset:簡單粗暴的理解就是數組下標。message queue是無限長的數組,每次消息進來就會漲1,下標就是offset。consumer可以通過指定offse位置開始讀取數據。queue的maxOffset是消息的最大offset,不是最新消息的offset 而是最新消息的offset+1,minOffset則是現存的最小offset。
2. Rocketmq消息存儲(順序寫,隨機讀)
消息存儲是由ConsumeQueue和CommitLog配合完成的。一個Topic里面有多個MessageQueue,每個MessageQueue對應一個ConsumeQueue.
默認地址:store/consumequeue/{topicName}/{queueid}/fileName
ConsumeQueue里記錄着消息物理存儲地址。(讀:consumer根據消息的consumeQueue找到消息存儲具體路徑,從而讀取里面信息)
CommitLog就存儲文件具體的字節信息。(寫:文件大小默認1g,文件名稱20位數 左邊補0右邊為偏移量。消息順序寫入文件,文件滿了則寫入下一個文件)
3. ZeroCopy高性能零拷貝
linux有兩個上下文(內核態、用戶態), 傳統的將一個file讀取並發送出去會經歷4個過程。
read時:
1. 將文件從磁盤copy到kernel(內核)態
2. cpu將kernrl態的數據copy到user(用戶)態
write時:
3. user態的內容會copy到kernel態的socket的buffer中
4. 將kernel中buffer的數據copy到網卡中傳送
我們可以發現2、3完全是多余的步驟,而且上下文之間的切換是很耗性能的。
ZeroCopy:內核直接把磁盤的數據傳輸到socket,而不是通過應用程序去傳輸。減少了不必要的內核緩沖區和用戶緩沖區間的拷貝,從而提升了性能。
零拷貝技術有mmap及sendfile;sendfile大文件傳輸快,mmap小文件傳輸快。MMQ發送的消息通常都很小,rocketmq就是以mmap+write方式實現的。像kafka、netty都采用了零拷貝技術。