MQ是英文Message Queue的縮寫,翻譯成中文是消息隊列。所謂隊列,是基礎數據結構中「先進先出」的一種數據結構。而消息隊列,是指在不一樣服務之間傳播消息。前端
爲何須要MQ?數據庫
技術上任何東西存在要有價值,就必須有使用的場景。咱們考慮一個平常的場景,你在網上A商城買東西,下單付款,這時候A給你推送了一條短信,告之你付費購買了XX;而且A還給你發了郵件,告之你付費購買了XX。服務器
在上述情形中,假設A的交易體量很大,因此下單付款和發短信、發郵件屬於不一樣的服務。每當下單成功了,訂單服務就要向外發消息,讓發短信服務和發郵件服務去把短信和郵件發出去。網絡
這時候就須要在服務之間傳播消息,因此須要MQ。數據結構
傳播消息直接HTTP不行麼?爲何要用MQ?併發
服務之間直接使用HTTP協議傳播消息,只能選擇RESTful的接口來實現。這種接口方式,對於消息隊列的傳輸來講,過重了。因此並非說這種辦法作不了,而是面向API的接口開發,要麼每一次接口改動就去修改代碼,這很不方便。並且,你若是想知道短信有沒有發出去,就必須讓短信服務再給你回一個接口,代碼量就很誇張。負載均衡
這個時候,不如專門抽出來一箇中間件去關心短信有沒有發出去、訂單服務發出的消息是否重複、訂單服務發出的消息是否順序正確這種事情。此時業務服務自己只須要關心發和接受消息就行了。分佈式
因此,爲了方便企業IT系統內部服務之間的通訊,咱們就須要單獨把這個事情拿出來作成一箇中間件——即消息隊列,一方面接受消息,一方面把消息發出去。這樣的好處是能把與消息相關的技術一併處理,還能下降服務之間的耦合性。spa
MQ只是單純的服務之間傳播消息麼?翻譯
MQ的核心功能是服務之間傳播消息,可是爲了保障消息傳播的可靠,咱們須要考慮諸如RPC、高可用、順序和重複消息、可靠投遞、消費關係解析等等這類問題。
當你須要使用消息隊列時,首先須要考慮它的必要性。可使用mq的場景有不少,最經常使用的幾種,是作業務解耦/最終一致性/廣播/錯峯流控等。反之,若是須要強一致性,關注業務邏輯的處理結果,則RPC顯得更爲合適。
解耦
解耦是消息隊列要解決的最本質問題。所謂解耦,簡單點講就是一個事務,只關心核心的流程。而須要依賴其餘系統但不那麼重要的事情,有通知便可,無需等待結果。換句話說,基於消息的模型,關心的是「通知」,而非「處理」。
好比上面的例子中,上游的訂單服務其實只關心Orders表裏有沒有正確的插入數據,根本不關心下游的短信、郵件服務的消息處理結果。
正如剛纔分析的,若是下游的短信、郵件服務定時來拉取數據,也能保證數據的更新,只是實時性沒有那麼強。但使用接口方式去發短信、郵件,顯然對於這些服務來講太過於「重量級」了,只須要發佈一個訂單建立的通知,由下游系統來處理,可能更爲合理。
再舉一個例子,公司的運營確定但願能看到天天的訂單數據,從而進行分析。可是訂單系統明顯也不關心這種業務,這時候選擇發一個通知給消息中心讓下游的展現系統讓他們去完成本身的業務,是最好的選擇了。
最終一致性
最終一致性指的是兩個系統的狀態保持一致,要麼都成功,要麼都失敗。固然有個時間限制,理論上越快越好,但實際上在各類異常的狀況下,可能會有必定延遲達到最終一致狀態,但最後兩個系統的狀態是同樣的。
業界有一些爲「最終一致性」而生的消息隊列,如Notify(阿里)、QMQ(去哪兒)等,其設計初衷,就是爲了交易系統中的高可靠通知。
以一個銀行的轉帳過程來理解最終一致性,轉帳的需求很簡單,若是A系統扣錢成功,則B系統加錢必定成功。反之則一塊兒回滾,像什麼都沒發生同樣。
然而,這個過程當中存在不少可能的意外:
可見,想把這件看似簡單的事真正作成,真的不那麼容易。全部跨VM的一致性問題,從技術的角度講通用的解決方案是:
最終一致性不是消息隊列的必備特性,但確實能夠依靠消息隊列來作最終一致性的事情。另外,全部不保證100%不丟消息的消息隊列,理論上沒法實現最終一致性。好吧,應該說理論上的100%,排除系統嚴重故障和bug。
像Kafka一類的設計,在設計層面上就有丟消息的可能(好比定時刷盤,若是掉電就會丟消息)。哪怕只丟千分之一的消息,業務也必須用其餘的手段來保證結果正確。
廣播
消息隊列的基本功能之一是進行廣播。若是沒有消息隊列,每當一個新的業務方接入,咱們都要聯調一次新接口。有了消息隊列,咱們只須要關心消息是否送達了隊列,至於誰但願訂閱,是下游的事情,無疑極大地減小了開發和聯調的工做量。
好比本文開始提到的產品中心發佈產品變動的消息,以及景點庫不少去重更新的消息,可能「關心」方有不少個,但產品中心和景點庫只須要發佈變動消息便可,誰關心誰接入。
錯峯與流控
試想上下游對於事情的處理能力是不一樣的。好比,Web前端每秒承受上千萬的請求,並非什麼神奇的事情,只須要加多一點機器,再搭建一些LVS負載均衡設備和Nginx等便可。但數據庫的處理能力卻十分有限,即便使用SSD加分庫分表,單機的處理能力仍然在萬級。因爲成本的考慮,咱們不能奢求數據庫的機器數量追上前端。
這種問題一樣存在於系統和系統之間,如短信系統可能因爲短板效應,速度卡在網關上(每秒幾百次請求),跟前端的併發量不是一個數量級。但用戶晚上個半分鐘左右收到短信,通常是不會有太大問題的。若是沒有消息隊列,兩個系統之間經過協商、滑動窗口等複雜的方案也不是說不能實現。但系統複雜性指數級增加,勢必在上游或者下游作存儲,而且要處理定時、擁塞等一系列問題。並且每當有處理能力有差距的時候,都須要單獨開發一套邏輯來維護這套邏輯。因此,利用中間系統轉儲兩個系統的通訊內容,並在下游系統有能力處理這些消息的時候,再處理這些消息,是一套相對較通用的方式。
總而言之,消息隊列不是萬能的。對於須要強事務保證並且延遲敏感的,RPC是優於消息隊列的。
對於一些無關痛癢,或者對於別人很是重要可是對於本身不是那麼關心的事情,能夠利用消息隊列去作。
支持最終一致性的消息隊列,可以用來處理延遲不那麼敏感的「分佈式事務」場景,並且相對於笨重的分佈式事務,多是更優的處理方式。
當上下游系統處理能力存在差距的時候,利用消息隊列作一個通用的「漏斗」。在下游有能力處理的時候,再進行分發。
若是下游有不少系統關心你的系統發出的通知的時候,果斷地使用消息隊列吧。
MQ的主要流派分爲兩種,一種是有Broker的MQ,一種是沒有Broker的MQ。沒有Broker的MQ能夠當作一次RPC,有Broker的MQ能夠當作是把一次RPC變成2次或者3次RPC。
這個流派一般有一臺服務器做爲 Broker,全部的消息都經過它中轉。生產者把消息發送給它就結束本身的任務了,Broker 則把消息主動推送給消費者或者讓消費者從Broker拉取。
一個消息隊列配備Broker,無外乎要作兩件事情:
這種MQ的表明有kafka、RabbitMQ、JMS。因爲本篇文章並不具體講某個MQ,因此僅簡單說一下最火的kafka,以下圖:
kafka是一個發佈訂閱消息系統,由topic區分消息種類,每一個topic中能夠有多個partition,每一個kafka集羣有一個多個broker服務器組成,producer能夠發佈消息到kafka中,consumer能夠消費kafka中的數據。kafka就是生產者和消費者中間的一個暫存區,能夠保存一段時間的數據保證使用。zk在kafka中作元數據存儲以及partition的failover。在下一個大的kafka版本中,zk將會被去除。
這種流派的MQ就是單純的把消息發送當作是一次RPC了,主要的表明有ZeroMQ。
文章首發於:
九神帶你理解消息隊列MQ