消息隊列_rocketmq

Part I

1、如何選擇消息隊列?

(1) 如果對消息隊列功能和性能都沒有很高的要求,只需一個開箱即用易維護的產品,建議使用RabbitMQ。

  • 有良好的運維界面,僅僅只是使用消息隊列功能,用於異步和業務模塊解耦,對性能要求不是很高。

(2)如果系統使用消息隊列的場景是處理在線業務,比如在交易系統中用消息隊列傳遞訂單,那麼使用RocketMQ。

  • 阿里內部也是使用RocketMQ作爲支撐其業務的消息隊列,經歷多次“雙十一”考驗,它的性能、穩定性和可靠性都是值得信賴的。

  • 有非常活躍的中文社區,大多數問題都可以找到中文答案。使用Java語言開發,它的貢獻者大多數爲中國人,源代碼相對比較易懂或易進行擴展和二次開發。

  • RocketMQ對在線業務的響應時延做了很多優化,大多數情況下可以做到毫秒級的響應,如果你的應用場景很在意響應時延,那應該選擇使用RocketMQ。

(3)如果需要處理海量的消息,想收集日誌、監控信息或是前端埋點這類數據,或應用場景大量使用了大數據、流計算(做事後的統計分析)相關的開源產品,那kafka是最適合的。

2、MQ是用來幹嘛的?有什麼作用?

MQ其實就是一種系統,獨立部署,讓系統之間通過發消息和收消息來進行異步的調用

作用:提升性能、系統解耦、流量消峯

  • 提升性能:
    一個請求調用了A、B兩個系統,執行業務邏輯各需要20 、200毫秒,那麼處理這個請求一共需要220毫秒
    引入MQ後:發送消息給MQ的速度是很快的(沒有業務邏輯、沒有數據庫操作),所以引入MQ後,20多毫秒就可以返回結果給用戶了。
  • 系統解耦:
    系統A和系統B通過同步調用的模式耦合在了一起,一旦系統B出現故障,很可能會影響系統A也有故障
    而且系統A還得去關心繫統B的故障,去處理對應的異常,這是很麻煩的。
    引入MQ後:B如果出現了故障,對系統A根本沒影響,系統A也感覺不到,B自己處理自己的問題!
  • 流量消峯:
    如果高併發訪問系統A(A沒有數據庫操作),A調用B(B有數據庫操作),那麼瓶頸在B,因爲數據庫操作是比較耗時的。
    同樣的機器配置下,如果數據庫可以抗每秒6000請求,MQ至少可以抗每秒幾萬請求。因爲數據庫複雜,需要支持事務、複雜的SQL查詢等
    引入MQ後:A系統依賴支持高併發的MQ,B也依賴MQ,此時B可以用自己的合適的速度訪問MQ,即B系統流量被消峯了。整個系統的性能由A決定,而不速度慢的B決定

3、什麼是RabbitMQ?

RabbitMQ是一個由erlang開發的AMQP(Advanced Message Queue 高級消息隊列協議 )的開源實現,能夠實現異步消息處理。

4、什麼是RocketMQ?

RocketMQ是一個低延時、高可靠、可伸縮、易於使用的分佈式消息中間件,是由阿里巴巴開源捐獻給Apache的頂級項目。RocketMQ具有高吞吐、低延遲、海量消息堆積等優點,同時提供順序消息、事務消息、定時消息、消息重試於追蹤等功能,非常適合在電商、金融等領域使用。

5、RocketMQ包含了幾個核心部分?

NameServer集羣、Broker集羣、生產者、消費者

  • NameServer
    負責管理所有的Broker消息
    讓生產者和消費者鬼知道集羣裏有哪些Broker,然後與之通信
  • Broker
    實現數據多副本存儲和高可用,使用主從架構
  • 生產者
    向MQ發送消息
  • 消費者
    從MQ獲取消息

6、RocketMQ持久化原理

現代的磁盤都是高性能的,寫速度並不一定比網絡的數據傳輸速度慢。比如 SSD 固態硬盤順序寫的速度可以達到 1500 MB/s,就算是普通磁盤,如果性能比較高的話,順序寫的速度可以達到 450MB/s~600MB/s。但是這是順序寫的速度。但實際上磁盤採用的是隨機寫,在隨機寫的情況下,磁盤的寫入速度急速下降,磁盤的隨機寫速度可能只有幾百KB/s,這遠遠要慢於網絡傳輸速度,所以它並不能滿足高性能的要求。
RocketMQ 在持久化的設計上,採取的是消息順序寫、隨機讀的策略,利用磁盤順序寫的速度,讓磁盤的寫速度不會成爲系統的瓶頸。並且採用 MMPP 這種“零拷貝”技術,提高消息存盤和網絡發送的速度。極力滿足 RocketMQ 的高性能、高可靠要求。
在 RocketMQ 持久化機制中,涉及到了三個角色:
CommitLog:消息真正的存儲文件,所有消息都存儲在 CommitLog 文件中。
ConsumeQueue:消息消費邏輯隊列,類似數據庫的索引文件。
IndexFile:消息索引文件,主要存儲消息 Key 與 offset 對應關係,提升消息檢索速度。
寫入:CommitLog -->文件是存放消息數據的地方,所有的消息都將存入到 CommitLog 文件中。生產者將消息發送到 RocketMQ 的 Broker 後,Broker 服務器會將消息順序寫入到 CommitLog 文件中,這也就是 RocketMQ 高性能的原因,因爲我們知道磁盤順序寫特別快,RocketMQ 充分利用了這一點,極大的提高消息寫入效率。
讀取:Queue–>但是同一主題的消息在 CommitLog 文件中可能是不連續的,那麼消費者消費消息的時候,需要將 CommitLog 文件加載到內存中遍歷查找訂閱主題下的消息,頻繁的 IO 操作,性能就會急速下降。爲了解決這個問題,RocketMQ 引入了 Queue 文件。Queue 文件可以看作是索引文件,類似於 MySQL 中的二級索引。在存放了同一主題下的所有消息,消費者消費的時候只需要去對應的 Queue 組中取消息即可。

Part II

1、如何保證消息隊列的高可用?

高可用HA(High Availability)是分佈式系統架構設計中必須考慮的因素之一,它通常是指通過設計減少系統不能提供服務的時間。
RocketMQ 部署方式:

  • 單個 Master。這種方式風險較大,一旦Broker重啓或者宕機時,會導致整個服務不可用,不建議線上環境使用。
  • 多 Master 模式(2m-noslave)。一個集羣無 Slave,全是 Master,例如 2 個 Master 或者 3 個 Master
    優點:配置簡單,單個Master 宕機或重啓維護對應用無影響,在磁盤配置爲RAID10 時,即使機器宕機不可恢 復情況下,由與 RAID10磁盤非常可靠,消息也不會丟(異步刷盤丟失少量消息,同步刷盤一條不丟)。性能最高。
    缺點:單臺機器宕機期間,這臺機器上未被消費的消息在機器恢復之前不可訂閱,消息實時性會受到受到影響。
  • 多 Master 多 Slave 模式,異步複製(2m-2s-async)。每個 Master 配置一個 Slave,有多對Master-Slave,HA。採用異步複製方式,主備有短暫消息延遲,毫秒級。
    優點:即使磁盤損壞,消息丟失的非常少,且消息實時性不會受影響,因爲Master 宕機後,消費者仍然可以從 Slave消費,此過程對應用透明。不需要人工干預。性能同多 Master 模式幾乎一樣。
    缺點:Master 宕機,磁盤損壞情況,會丟失少量消息。
  • 多 Master 多 Slave 模式,同步雙寫(2m-2s-sync)。每個 Master 配置一個 Slave,有多對Master-Slave,HA。採用同步雙寫方式,主備都寫成功,嚮應用返回成功。
    優點:數據與服務都無單點,Master宕機情況下,消息無延遲,服務可用性與數據可用性都非常高
    缺點:性能比異步複製模式略低,大約低10%左右,發送單個消息的RT會略高。目前主宕機後,備機不能自動切換爲主機,後續會支持自動切換功能。

2、如何保證消息不被重複消費(如何保證消息消費時的冪等性)?

其實重複消費不可怕,可怕的是你沒考慮到重複消費之後,怎麼保證冪等性。

冪等性,通俗點說,就一個數據,或者一個請求,給你重複來多次,你得確保對應的數據是不會改變的,不能出錯。

其實還是得結合業務來思考,我這裏給幾個思路:

  • 比如你拿個數據要寫庫,你先根據主鍵查一下,如果這數據都有了,你就別插入了,update一下好吧
  • 比如你是寫redis,那沒問題了,反正每次都是set,天然冪等性
  • 比如你不是上面兩個場景,那做的稍微複雜一點,你需要讓生產者發送每條數據的時候,裏面加一個全局唯一的id,類似訂單id之類的東西,然後你這裏消費到了之後,先根據這個id去比如redis裏查一下,之前消費過嗎?如果沒有消費過,你就處理,然後這個id寫redis。如果消費過了,那你就別處理了,保證別重複處理相同的消息即可。

3、如何保證消息的可靠性傳輸(如何處理消息丟失的問題)?

可以從三個方面來分析rocket的消息可靠性

  • Producer端消息丟失
    首先消息分爲普通消息(同步,異步,單向發送),定時延時消息,順序消息(不能保證全局有序,只能保證同一個quen中有序),事物消息。
    producer端防止消息發送失敗,可以採用同步阻塞式的發送(也就是發送同步消息),同步的檢查Brocker返回的狀態是否持久化成功,發送超時或者失敗,則會默認重試2次,rocker選擇了確保消息一定發送成功,但有可能發生重複投遞。
    如果是異步發送消息,會有一個回調接口,當brocker存儲成功或者失敗的時候,也可以在這裏根據返回狀態來決定是否需要重試(當然這個是需要我們自己來實現的)。
  • Brocker端消息丟失
    rocketmq一般都是先把消息寫到PageCache中,然後再持久化到磁盤上,數據從pagecache刷新到磁盤有兩種方式,同步和異步。
    同步刷盤方式:消息寫入內存的 PageCache後,立刻通知刷盤線程刷盤,然後等待刷盤完成,刷盤線程執行完成後喚醒等待的線程,返回消息寫成功的狀態。這種方式可以保證數據絕對安全,但是吞吐量不大。
    異步刷盤方式(默認):消息寫入到內存的 PageCache中,就立刻給客戶端返回寫操作成功,當 PageCache中的消息積累到一定的量時,觸發一次寫操作,將 PageCache中的消息寫入到磁盤中。這種方式吞吐量大,性能高,但是 PageCache中的數據可能丟失,不能保證數據絕對的安全。
  • Cousmer端消息丟失
    cousmer端默認是消息之後自動返回消費成功確認ack,但是這時如果我們的程序執行失敗了,數據不就丟失了嗎?
    所以我們可以將自動提交(AutoCommit)消費響應,設置爲在代碼中手動提交,只有真正消費成功之後再通知brocker消費成功,然後更新消費唯一offset或者刪除brocker中的消息。

4、如何保證消息的順序性?

  • rabbitmq:拆分多個queue,每個queue一個consumer,就是多一些queue而已,確實是麻煩點;或者就一個queue但是對應一個consumer,然後這個consumer內部用內存隊列做排隊,然後分發給底層不同的worker來處理
  • rocketmq:同一條queue裏面保證FIFO的。把消息確保投遞到同一條queue。一個訂單的順序流程是:創建、付款、推送、完成。訂單號相同的消息會被先後發送到同一個隊列中,消費時,同一個OrderId獲取到的肯定是同一個隊列。

5、如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以後該怎麼處理?

(1)一般這個時候,只能操作臨時緊急擴容了,具體操作步驟和思路如下:

  • 先修復consumer的問題,確保其恢復消費速度,然後將現有cnosumer都停掉;
  • 新建一個topic,partition是原來的10倍,臨時建立好原先10倍或者20倍的queue數量;
  • 然後寫一個臨時的分發數據的consumer程序,這個程序部署上去消費積壓的數據,消費之後不做耗時的處理,直接均勻輪詢寫入臨時建立好的10倍數量的queue‘;
  • 接着臨時徵用10倍的機器來部署consumer,每一批consumer消費一個臨時queue的數據;
  • 這種做法相當於是臨時將queue資源和consumer資源擴大10倍,以正常的10倍速度來消費數據;
  • 等快速消費完積壓數據之後,得恢復原先部署架構,重新用原先的consumer機器來消費消息。

假設你用的是rabbitmq,rabbitmq是可以設置過期時間的,就是TTL,如果消息在queue中積壓超過一定的時間就會被rabbitmq給清理掉,這個數據就沒了。那這就是第二個坑了。這就不是說數據會大量積壓在mq裏,而是大量的數據會直接搞丟。
假設1萬個訂單積壓在mq裏面,沒有處理,其中1000個訂單都丟了,你只能手動寫程序把那1000個訂單給查出來,手動發到mq裏去再補一次。

(2)如果走的方式是消息積壓在mq裏,那麼如果你很長時間都沒處理掉,此時導致mq都快寫滿了,咋辦?這個還有別的辦法嗎?沒有,誰讓你第一個方案執行的太慢了,你臨時寫程序,接入數據來消費,消費一個丟棄一個,都不要了,快速消費掉所有的消息。

6、如果讓你寫一個消息隊列,該如何進行架構設計啊?說一下你的思路。

比如說這個消息隊列系統,我們來從以下幾個角度來考慮一下:

  • 可伸縮性。就是需要的時候快速擴容,就可以增加吞吐量和容量,參照一下kafka的設計理念,broker -> topic -> partition,每個partition放一個機器,就存一部分數據。如果現在資源不夠了,簡單啊,給topic增加partition,然後做數據遷移,增加機器,不就可以存放更多數據,提供更高的吞吐量了。
  • 高可用性。
  • 高可靠性。考慮一下這個mq的數據要不要落地磁盤吧?那肯定要了,落磁盤,才能保證別進程掛了數據就丟了。那落磁盤的時候怎麼落啊?順序寫,這樣就沒有磁盤隨機讀寫的尋址開銷,磁盤順序讀寫的性能是很高的,這就是kafka的思路。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章