MQ消息隊(duì)列使用進(jìn)階大全

使用Mq有哪些優(yōu)點(diǎn)?

  • 解耦:A系統(tǒng)依賴B,C,D系統(tǒng),A只需要發(fā)送topic,B,C,D后期不需要消息自行取消消費(fèi)即可,類似消息總線
  • 異步:異步執(zhí)行消息發(fā)送,通知等
  • 消峰:應(yīng)對突發(fā)的流量高峰時(shí)段(錯(cuò)峰與流控)
  • 復(fù)用:一次發(fā)送多次消費(fèi)

使用Mq有哪些缺點(diǎn)?

  • 系統(tǒng)復(fù)雜度提高
  • 系統(tǒng)可用性降低

Mq怎么選型,各個(gè)Mq中間件的優(yōu)缺點(diǎn)?

  • 開源產(chǎn)品,市場任何度
  • 消息可靠性
  • 跨語言支持
  • 性能
  • 功能
image

使用Mq會(huì)帶來哪些問題?

一致性問題

使用可靠消息最終一致性的分布式事務(wù)方案來保障

消息順序問題

一筆訂單產(chǎn)生了3條消息,分別是訂單創(chuàng)建、訂單付款、訂單完成。消費(fèi)時(shí),要按照順序依次消費(fèi)才有意義

出現(xiàn)原因:

  • 一個(gè)queue,有多個(gè)consumer去消費(fèi)。因?yàn)槊總€(gè)consumer的執(zhí)行時(shí)間是不固定的,先讀到消息的consumer不一定先完成操作
  • 一個(gè)queue對應(yīng)一個(gè)consumer,但是consumer里面進(jìn)行了多線程消費(fèi),這樣也會(huì)造成消息消費(fèi)順序錯(cuò)誤

解決:

  • 有關(guān)聯(lián)的同一組消息1、2、3發(fā)到queue1中,然后消費(fèi)者1消費(fèi),因?yàn)橄㈥?duì)列本來就是有序的,所以這樣就有序。為了提高性能,搞多個(gè)queue,有關(guān)聯(lián)的同一組消息發(fā)到同一隊(duì)列,每個(gè)隊(duì)列都有唯一的消費(fèi)者
  • 一個(gè)queue但是對應(yīng)一個(gè)consumer,然后這個(gè)consumer內(nèi)部用內(nèi)存隊(duì)列做排隊(duì),然后分發(fā)給底層不同的worker來處理。原理和上面一樣,都是保證同一組消息發(fā)給同一隊(duì)列,然后被同一消費(fèi)者消費(fèi)
  • 既然要求“同一組消息發(fā)給同一隊(duì)列,然后被同一消費(fèi)者消費(fèi)”,那最好的辦法是把同一組消息合并成一條。這樣性能更好,無論多線程、還是多消費(fèi)者都o(jì)k。
    有人可能說合并后會(huì)不會(huì)數(shù)據(jù)量太大?
    大多數(shù)場景都不要求順序執(zhí)行。比如電商支付完后需要:App推送、短信推送、加積分、給倉庫發(fā)發(fā)貨的消息、修改購物推薦;比如支付寶搶紅包,先搶到的不要求先到賬。

總結(jié):涉及到發(fā)送方集群,mq集群,接收方集群

  • 保證發(fā)送方是順序發(fā)送
  • 保證同一個(gè)唯一標(biāo)識(訂單號)只發(fā)送到指定Mq Server上
  • 保證同一個(gè)唯一標(biāo)識(訂單號)只在一個(gè)接收方節(jié)點(diǎn)上消費(fèi)

消息堆積問題

  • 臨時(shí)擴(kuò)容,以更快的速度去消費(fèi)數(shù)據(jù)了,先修復(fù)consumer的問題,確保其恢復(fù)消費(fèi)速度,然后將現(xiàn)有consumer都停掉。臨時(shí)建立好原先10倍或者20倍的queue數(shù)量(新建一個(gè)topic,partition是原來的10倍)。然后寫一個(gè)臨時(shí)分發(fā)消息的consumer程序,這個(gè)程序部署上去消費(fèi)積壓的消息,消費(fèi)之后不做耗時(shí)處理,直接均勻輪詢寫入臨時(shí)建好分10數(shù)量的queue里面。緊接著征用10倍的機(jī)器來部署consumer,每一批consumer消費(fèi)一個(gè)臨時(shí)queue的消息。
    這種做法相當(dāng)于臨時(shí)將queue資源和consumer資源擴(kuò)大10倍,以正常速度的10倍來消費(fèi)消
    等快速消費(fèi)完了之后,恢復(fù)原來的部署架構(gòu),重新用原來的consumer機(jī)器來消費(fèi)消息


    image
  • 臨時(shí)寫個(gè)程序,連接到mq里面消費(fèi)數(shù)據(jù),收到消息之后直接存redis,后續(xù)重發(fā)
  • mq完全放不下,快掛了,寫個(gè)程序,連接到mq里面消費(fèi)數(shù)據(jù),收到消息之后直接將其丟棄,快速消費(fèi)掉積壓的消息,降低MQ的壓力,然后走第二種方案
  • 堆積引起的丟失,高峰期過后手動(dòng)去查詢丟失的那部分?jǐn)?shù)據(jù),然后將消息重新發(fā)送到mq里面,把丟失的數(shù)據(jù)重新補(bǔ)回來

消息重復(fù)發(fā)送問題(消息冪等性)

uuid-redis,唯一索引,狀態(tài)機(jī)判斷

消息丟失問題

  • rabbitMq
生產(chǎn)者:
開啟confirm模式(異步),寫的消息都會(huì)分配一個(gè)唯一的id,rabbitmq會(huì)給你回傳一個(gè)ack消息

mq server:
queue持久化,可以保證rabbitmq持久化queue的元數(shù)據(jù),但是不會(huì)持久化queue里面的數(shù)據(jù)
deliveryMode設(shè)置為2,這樣消息就會(huì)被設(shè)為持久化方式,此時(shí)rabbitmq就會(huì)將消息持久化到磁盤上。
就算是在持久化之前rabbitmq掛了,數(shù)據(jù)丟了,生產(chǎn)者收不到ack回調(diào)也會(huì)進(jìn)行消息重發(fā)

消費(fèi)者:
使用rabbitmq提供的ack機(jī)制,首先關(guān)閉rabbitmq的自動(dòng)ack,然后每次在確保處理完這個(gè)消息之后,
在代碼里手動(dòng)調(diào)用ack。這樣就可以避免消息還沒有處理完就ack
  • kafka
消費(fèi)者:
關(guān)閉自動(dòng)提交offset,在自己處理完畢之后手動(dòng)提交offset,這樣就不會(huì)丟失數(shù)據(jù)。

mq server:
一般要求設(shè)置4個(gè)參數(shù)來保證消息不丟失:
給topic設(shè)置 replication.factor參數(shù):這個(gè)值必須大于1,要求每個(gè)partition必須至少有2個(gè)副本。
在kafka服務(wù)端設(shè)置min.isync.replicas參數(shù):這個(gè)值必須大于1, 要求一個(gè)leader至少感知到有至少一個(gè)follower
在跟自己保持聯(lián)系正常同步數(shù)據(jù),這樣才能保證leader掛了之后還有一個(gè)follower。
在生產(chǎn)者端設(shè)置acks=all:表示要求每條每條數(shù)據(jù),必須是寫入所有replica副本之后,才能認(rèn)為是寫入成功了
在生產(chǎn)者端設(shè)置retries=MAX(很大的一個(gè)值,表示無限重試):表示 這個(gè)是要求一旦寫入事變,就無限重試

生產(chǎn)者:
如果按照上面設(shè)置了ack=all,則一定不會(huì)丟失數(shù)據(jù),要求是,你的leader接收到消息,
所有的follower都同步到了消息之后,才認(rèn)為本次寫成功了。如果沒滿足這個(gè)條件,
生產(chǎn)者會(huì)自動(dòng)不斷的重試,重試無限次

Mq集群方式

  • rabbitMq
  1. 單機(jī)模式
  2. 普通集群 每次寫消息到queue的時(shí)候,都會(huì)自動(dòng)把消息到多個(gè)queue里進(jìn)行消息同步,每個(gè)節(jié)點(diǎn)都有queue隊(duì)列的元數(shù)據(jù),還需要去對應(yīng)節(jié)點(diǎn)拉數(shù)據(jù)再返回
  3. 鏡像集群模式 每次寫消息到queue的時(shí)候,都會(huì)自動(dòng)把消息到多個(gè)queue里進(jìn)行消息同步,每個(gè)節(jié)點(diǎn)都有queue隊(duì)列的完整數(shù)據(jù)
  • kafka
  1. 多個(gè)broker組成,一個(gè)broker是一個(gè)節(jié)點(diǎn);你創(chuàng)建一個(gè)topic,這個(gè)topic可以劃分成多個(gè)partition,每個(gè)partition可以存在于不同的broker上面,每個(gè)partition存放一部分?jǐn)?shù)據(jù)。這是天然的分布式消息隊(duì)列

Mq知識點(diǎn)

數(shù)據(jù)交互模式:

Push(推模式)、Pull(拉模式)

push更關(guān)注實(shí)時(shí)性,pull更關(guān)注消費(fèi)者消費(fèi)能力

推模式指的是客戶端與服務(wù)端建立好網(wǎng)絡(luò)長連接,服務(wù)方有相關(guān)數(shù)據(jù),直接通過長連接通道推送到客戶端。其優(yōu)點(diǎn)是及時(shí),一旦有數(shù)據(jù)變更,客戶端立馬能感知到;另外對客戶端來說邏輯簡單,不需要關(guān)心有無數(shù)據(jù)這些邏輯處理。缺點(diǎn)是不知道客戶端的數(shù)據(jù)消費(fèi)能力,可能導(dǎo)致數(shù)據(jù)積壓在客戶端,來不及處理。

拉模式指的是客戶端主動(dòng)向服務(wù)端發(fā)出請求,拉取相關(guān)數(shù)據(jù)。其優(yōu)點(diǎn)是此過程由客戶端發(fā)起請求,故不存在推模式中數(shù)據(jù)積壓的問題。缺點(diǎn)是可能不夠及時(shí),對客戶端來說需要考慮數(shù)據(jù)拉取相關(guān)邏輯,何時(shí)去拉,拉的頻率怎么控制等等。

拉模式中,為了保證消息消費(fèi)的實(shí)時(shí)性,采取了長輪詢消息服務(wù)器拉取消息的方式。每隔一定時(shí)間,客戶端想服務(wù)端發(fā)起一次請求,服務(wù)端有數(shù)據(jù)就返回?cái)?shù)據(jù),服務(wù)端如果此時(shí)沒有數(shù)據(jù),保持連接。等到有數(shù)據(jù)返回(相當(dāng)于一種push),或者超時(shí)返回。長輪詢Pull的好處就是可以減少無效請求,保證消息的實(shí)時(shí)性,又不會(huì)造成客戶端積壓。

推模式是最常用的,但是有些情況下推模式并不適用的,比如說:
由于某些限制,消費(fèi)者在某個(gè)條件成立時(shí)才能消費(fèi)消息
需要批量拉取消息進(jìn)行處理

rabbitMq推模式實(shí)現(xiàn)SimpleMessageListenerContainer 拉模式:basicGet

消費(fèi)關(guān)系處理

  • 單播,就是點(diǎn)到點(diǎn)
  • 廣播,是一點(diǎn)對多點(diǎn),一個(gè)生產(chǎn)者,對應(yīng)對個(gè)消費(fèi)者消費(fèi)-發(fā)布訂閱Publish\Subscribe

為了實(shí)現(xiàn)廣播功能,我們必須要維護(hù)消費(fèi)關(guān)系,通常消息隊(duì)列本身不維護(hù)消費(fèi)訂閱關(guān)系,可以利用zookeeper等成熟的系統(tǒng)維護(hù)消費(fèi)關(guān)系,在消費(fèi)關(guān)系發(fā)生變化時(shí)下發(fā)通知

rabbitMq

image
工作模式
Exchange有常見以下3種類型:
Fanout:廣播,將消息交給所有綁定到交換機(jī)的隊(duì)列
Direct:組播,定向,把消息交給符合指定routing key 的隊(duì)列
Topic:規(guī)則播,通配符,把消息交給符合routing pattern(路由模式) 的隊(duì)列
Header

1、簡單模式 HelloWorld
一個(gè)生產(chǎn)者、一個(gè)消費(fèi)者,不需要設(shè)置交換機(jī)(使用默認(rèn)的交換機(jī))

2、工作隊(duì)列模式 Work Queue
一個(gè)生產(chǎn)者、多個(gè)消費(fèi)者(競爭關(guān)系),不需要設(shè)置交換機(jī)(使用默認(rèn)的交換機(jī))

3、發(fā)布訂閱模式 Publish/subscribe--對應(yīng)廣播-消息總線
需要設(shè)置類型為fanout的交換機(jī),并且交換機(jī)和隊(duì)列進(jìn)行綁定,當(dāng)發(fā)送消息到交換機(jī)后,交換機(jī)會(huì)將消息發(fā)送到綁定的隊(duì)列

4、路由模式 Routing
需要設(shè)置類型為direct的交換機(jī),交換機(jī)和隊(duì)列進(jìn)行綁定,并且指定routing key,當(dāng)發(fā)送消息到交換機(jī)后,交換機(jī)會(huì)根據(jù)routing key將消息發(fā)送到對應(yīng)的隊(duì)列

5、通配符模式 Topic
需要設(shè)置類型為topic的交換機(jī),交換機(jī)和隊(duì)列進(jìn)行綁定,并且指定通配符方式的routing key,當(dāng)發(fā)送消息到交換機(jī)后,交換機(jī)會(huì)根據(jù)routing key將消息發(fā)送到對應(yīng)的隊(duì)列

優(yōu)點(diǎn):

  • 消費(fèi)方可以選擇消費(fèi)方式為pull或者是broker主動(dòng)push
  • 支持的消費(fèi)模式也有多種,點(diǎn)對點(diǎn),廣播,正則匹配,訂閱發(fā)布
  • 消息需要通過復(fù)雜的路由到消費(fèi)者
  • 性能20k/sec

缺點(diǎn):

  • 吞吐量不高,分布式弱

Kafka

image

image

image
Kafka是一種高吞吐量的分布式發(fā)布訂閱消息系統(tǒng),使用Scala編寫。默認(rèn)端口9092
依賴zk,每個(gè)topic里面都有一個(gè)leader,topic,leader信息存zk。
每個(gè)分區(qū)是有順序性,可以將一類,比如某個(gè)人的訂單hash到同一分區(qū)(就可以保證順序性,可以制定某個(gè)key來hash)
Topic一種邏輯分類的概念到不同的分區(qū)(類似queue),消息不會(huì)刪除,默認(rèn)8天自動(dòng)刪除
一個(gè)分區(qū)對應(yīng)一個(gè)消費(fèi)者,單播
可以用消費(fèi)組來實(shí)現(xiàn)多播,1個(gè)分區(qū)對應(yīng)多個(gè)消費(fèi)組
備份因子:必須小于等于節(jié)點(diǎn)數(shù)
kafka選舉原理:就是利用zk臨時(shí)節(jié)點(diǎn),斷開即刪除,
然后flower監(jiān)聽watcher的副節(jié)點(diǎn)有變化就重新創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn),誰建成功誰就是leader

優(yōu)點(diǎn):

  • 從A系統(tǒng)到B系統(tǒng)的消息沒有復(fù)雜的傳遞規(guī)則,并且具有較高的吞吐量要求。性能100k/sec
  • 需要訪問消息的歷史記錄的場景,因?yàn)閗afak是持久化消息的,所以可以通過偏移量訪問到那些已經(jīng)被消費(fèi)的消息
  • 流處理的場景。處理源源不斷的流式消息
  • 高性能原因:集群。消息是順序存磁盤(比隨機(jī)內(nèi)存性能高)。消息消費(fèi)不需要ack刪除消息。全異步

缺點(diǎn):

  • 客戶端發(fā)送一條消息的時(shí)候,Kafka并不會(huì)立即發(fā)送出去,先攢一波再一起處理,Kafka 不太適合在線業(yè)務(wù)場景

自己設(shè)計(jì)一個(gè)Mq需要考慮哪些東西?

  • 優(yōu)先級隊(duì)列,延遲隊(duì)列,死性隊(duì)列,重試隊(duì)列
  • 消息回蹤,消息丟失
  • 跨語言,安全機(jī)制,多協(xié)議支持
  • 消費(fèi)模式
  • 消費(fèi)關(guān)系處理
  • 可以參考Pulsar,存儲和計(jì)算分離的設(shè)計(jì)

參考:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 夜鶯2517閱讀 128,171評論 1 9
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,849評論 28 54
  • 兔子雖然是枚小碩 但學(xué)校的碩士四人寢不夠 就被分到了博士樓里 兩人一間 在學(xué)校的最西邊 靠山 兔子的室友身體不好 ...
    待業(yè)的兔子閱讀 2,771評論 2 9
  • 信任包括信任自己和信任他人 很多時(shí)候,很多事情,失敗、遺憾、錯(cuò)過,源于不自信,不信任他人 覺得自己做不成,別人做不...
    吳氵晃閱讀 6,372評論 4 8

友情鏈接更多精彩內(nèi)容