掌握高併發、高可用架構
第三章 分佈式
本章介紹分佈式架構的底層技術。主要說明面試過程中可能被問到的技術點。
第三節 消息隊列(MQ,Message Queue)
消息隊列
MQ
JMS
AMQP
ActiveMQ
RabbitMQ
RocketMQ
kafka
1. 什麼是消息隊列
可以把消息隊列簡單理解爲存放消息的容器,並且當我們需要消息的時候可以取出消息供自己使用。消息隊列是分佈式系統中重要的中間件,它可以通過異步操作來實現提高系統性能、流量削峯、降低系統耦合。
目前比較流行的消息隊列有:ActiveMQ、RabbitMQ、RocketMQ、Kafka
2. 爲何要用消息隊列,它的優勢
消息隊列的優勢主要體現在:
-
通過異步處理來提高系統性能
使用了消息隊列後,用戶的請求發送到消息隊列後就返回了,由消息隊列的消費者把消息異步的寫到數據庫。由於消息隊列服務器的處理速度大於數據庫,所以系統性能大幅提高
-
降低系統的耦合性
使用了消息隊列後,應用之間不再是強耦合關係,各個應用從消息隊列獲取需要的消息,從而降低耦合
-
限流削峯
購物網站的秒殺活動,在秒殺開始的一瞬間流量巨大,可能會導致系統無法處理而崩潰。加入消息隊列後,用戶請求都會放入消息隊列,系統處理則從消息隊列獲取數據,不至於導致系統崩潰
- 請求先入消息隊列,而不是直接交給業務處理,做了一次緩衝,極大的降低了系統的壓力
- 隊列長度可以進行限制,後面的請求無法入隊,就可以直接拋棄了
3. 使用消息隊列帶來的問題
- 系統可用性降低:加入MQ後,需要考慮服務器宕機的問題
- 系統複雜性提高:消息丟失、重複消費、消息傳遞的順序性
- 一致性問題:消息被正確的消費
4. JMS VS AMQP
JMS,Java Message Service即Java消息服務,它是消息服務的規範。JMS API允許應用程序組件基於JMEE平臺創建 、發送、接收、讀取消息。它使分佈式通信耦合性降低、消息服務更加可靠
ActiveMQ就是基於JMS規範實現的
JMS支持兩種消息模型:
-
點對點(P2P)模型
使用隊列(Queue)來滿足生產者消費者模式,一條消息只能被一個消費者消費
-
發佈/訂閱(Pub/Sub)模型
發佈-訂閱模型使用主題(Topic)作爲消息通信載體,發佈者發佈一條消息,該消息通過主題傳遞給所有的訂閱者
JMS支持的五種消息格式
- StreamMessage,Java原始值的數據流
- MapMessage,鍵值對
- TextMessage,字符串
- ObjectMessage,序列化的對象
- BytesMessage,字節數據流
AMQP,Advanced Message Queuing Protocol,高級消息隊列協議(二進制應用層協議)。它是一個協議,兼容JMS。基於它實現的消息隊列,不收客戶端、中間件的開發語言限制
RabbitMQ就是基於AMQP實現的
比較內容 | JMS | AMQP |
---|---|---|
定義 | JMS API,是一種規範 | 是一個協議 |
跨語言 | 否,僅支持JAVA | 是 |
誇平臺 | 否 | 是 |
消息模型 | 點對點模型、發佈-訂閱模型 | 提供了五種消息模型:<br />1.direct exchange<br />2.fanout exchange<br />3.topic change<br />4.header exchange<br />5.system exchange<br />後四種和發佈訂閱僅是在路由機制上做了更詳細的劃分 |
消息類型 | 上述的五種消息格式 | byte[] 二進制 |
5. 消息隊列選型
比較目前流行的四種消息隊列
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
開發語言 | java | erlang | java | scala |
單機吞吐量 | 萬級(最差) | 萬級(其次) | 十萬級(最高) | 十萬級(次之) |
時效性 | ms級 | us級 | ms級 | ms以內 |
可用性 | 基於Zookeeper <br />+ LevelDB的<br />master-slave模式 | master/slave模式<br />master提供服務<br />slave僅做備份 | 多master模式<br />多master多slave異步複製模式<br />多master多slave同步雙寫模式 | replica機制,<br />leader宕機備份自動頂替,<br />並重新選舉leader(基於Zookeeper) |
功能特性 | 成熟產品,社區不活躍,文檔齊全 | 併發能力強,性能好,管理界面豐富 | 比較成熟,擴展性佳 | 只提供主要的MQ功能,在大數據領域應用廣 |
消息推拉 | pull,push都支持 | pull,push都支持 | pull,push都支持 | pull |
6. 如何保證不被重複消費
什麼情況會發生重複消費呢?正常情況下,消費者在消費消息完成後,會發送一個確認消息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除。因爲網絡傳輸等故障,導致確認消息沒有傳送到消息隊列,繼而消息隊列不知道該消息已被消費,再次將消息分發消費了
這個問題在不同的場景下有不同的解決方案,主要是在消費方來解決重複問題
- 消息用作DB的insert操作,此時可以把消息作爲一個主鍵,當出現重複消費時,就會主鍵衝突,自然就避免了
- 消息用作Redis的set操作,此時,因爲set操作會覆蓋上一個值 ,但是兩次的值一樣,也是無所謂的
- 最壞的情況下,可以準備第三方介質來存儲消費的消息。比如Redis,每次操作時都去Redis中查看是否之前操作過
7. 如何保證數據不丟失
消息丟失主要分爲:生產者丟失、消息隊列丟失、消費者丟失
每種MQ都有不同的處理,這裏就不詳細論述了