消息隊列
什么是消息隊列(Message Queue酒来,MQ)呢啡浊?
首先回憶下生活中在餐館點餐的場景,當你點完餐之后老板會給一個號牌陶因,每個人都按照自己付款拿到的號牌順序排隊等待叫號骡苞。實際上,這里的柜臺就充當著消息隊列的角色〗庥模客戶等待生產(chǎn)者把訂餐的消息發(fā)送到柜臺即消息隊列中后贴见,從中取餐即消費消息。整個流程實際上是消息被發(fā)送到隊列中亚铁,又成功被消費者消費的過程蝇刀。
回過頭來理解下消息隊列螟加,首先需要了解什么是消息徘溢,什么是隊列。
消息Message
消息是軟件對象之間交互和通訊的數(shù)據(jù)捆探,消息可以是簡單的文本字符串也可以是復(fù)雜的對象然爆。
消息作為消息隊列中最基本的概念,本質(zhì)上是一段數(shù)據(jù)黍图,能被一個或多個應(yīng)用程序所理解曾雕,是應(yīng)用程序之間傳遞信息的載體。
在消息隊列中助被,把應(yīng)用程序交由消息隊列傳輸?shù)臄?shù)據(jù)定義為消息剖张,可以定義消息的內(nèi)容并對消息進行廣義的理解。
消息由兩部分組成
- 消息描述符(
Message Description
或Message Handler
)
用于描述消息的特征揩环,如消息的優(yōu)先級搔弄、生命周期、消息ID等丰滑。 - 消息體(
Message Body
)
即用戶數(shù)據(jù)部分
在消息隊列中消息分為兩種類型
- 非永久性消息(
non-persistent
)
非永久性消息是存儲在內(nèi)存中顾犹,為了提高性能而設(shè)計的,當系統(tǒng)掉電或消息隊列管理器重新啟動時將不可恢復(fù)褒墨。如果用戶對消息的可靠性要求不高而側(cè)重系統(tǒng)的性能表現(xiàn)時炫刷,可采用此種類型的消息。 - 永久性消息(
persistent
)
永久性消息是存儲在磁盤上并記錄數(shù)據(jù)日志的郁妈,具有高可靠性浑玛,在網(wǎng)絡(luò)和系統(tǒng)發(fā)生故障等情況下能夠確保消息不丟失。
隊列Queue
隊列是一種特殊的線性表噩咪,和棧一樣是一種操作受限的線性表顾彰,特殊之處在于隊列只允許在表的頭部front
刪除在尾部rear
插入。進行插入操作的一端稱為隊尾rear
剧腻,進行刪除操作的一端稱為隊頭front
拘央。
隊列是一種先進先出的數(shù)據(jù)結(jié)構(gòu),隊列可以理解為管道书在,消息隊列也就是以管道的方式做消息傳遞灰伟。
例如:Redis提供的list
數(shù)據(jù)結(jié)構(gòu)就非常適合做消息隊列,Redis中的List
本質(zhì)上是一個雙向鏈表。zset
有序集合也可以用來做消息隊列的容器栏账。
Redis中可以使用自帶的publish
和subscribe
命令完成消息推送和消息拉取帖族,進而實現(xiàn)消息隊列。但這種方式有一個缺陷是消費者必須一直在線挡爵,否則會出現(xiàn)消費遺漏竖般。
使用Redis實現(xiàn)的輕量化的消息隊列有三大優(yōu)勢
- Redis已經(jīng)廣泛應(yīng)用于各大系統(tǒng)無需再次引入其它第三方框架和API
- Redis是基于內(nèi)存存儲的,生產(chǎn)者和消費者的存取速度都非常塊
- Redis集群的容量可以通過添加實例進行擴展
一個輕量化的消息隊列需要滿足的條件包括
- 消費順序保持跟生產(chǎn)順序一致
- 對于廣播消息茶鹃,某個消費者實例重啟后能重新收到消息
- 定時清理所有消費者都已經(jīng)消費過的數(shù)據(jù)涣雕,防止容量無限增長。
消息隊列可以簡單理解為將需要傳輸?shù)臄?shù)據(jù)放入隊列中闭翩,把數(shù)據(jù)放入消息隊列的一端稱為生產(chǎn)者(productor
)挣郭,從消息隊列中取出數(shù)據(jù)的一端叫做消費者(consumer
)。
消息隊列基本結(jié)構(gòu)
-
Broker
消息隊列服務(wù)器 -
Producer
消息生產(chǎn)者疗韵,發(fā)送消息到消息隊列 -
Consumer
消息消費者兑障,從消息隊列中接收消息
也可以將消息隊列視為一個存放消息的容器,當需要使用消息時可以從中取出供自己使用蕉汪。隊列的目的是提供路由并保證消息的傳遞流译,如果發(fā)送消息時接收者不可用,消息隊列會保留消息者疤,直到可以成功地傳遞它福澡。
簡單來說,消息隊列是指利用高效可靠的消息傳輸機制及運行平臺無關(guān)的數(shù)據(jù)交流宛渐,并基于數(shù)據(jù)通信來進行分布式系統(tǒng)的集成竞漾。
消息隊列通過提供消息傳遞和消息排隊模型,可以在分布式環(huán)境下提供應(yīng)用解耦窥翩、彈性伸縮业岁、冗余存儲、流量削峰寇蚊、異步通信笔时、數(shù)據(jù)同步等功能,其作為分布式系統(tǒng)架構(gòu)中的一個重要組件仗岸,有著舉足輕重的地位允耿。
消息隊列是分布式系統(tǒng)中重要的組件,應(yīng)用于當不需要立即獲得結(jié)果但并發(fā)量又需要進行控制時的場景扒怖,主要解決了應(yīng)用耦合较锡、異步處理、流量削峰等問題盗痒。消息隊列利用高效可靠的消息傳遞機制進行平臺無關(guān)的數(shù)據(jù)交流蚂蕴,并基于數(shù)據(jù)通信來進行分布式系統(tǒng)的集成。
消息隊列是一種異步的服務(wù)將通信方式,適用于無服務(wù)器和微服務(wù)架構(gòu)骡楼,消息在被處理和刪除之前一直存儲在隊列上熔号。每條消息僅可被一位用戶處理一次。消息隊列可被用于分離重量級處理鸟整、緩沖或批處理工作以及解耦高峰期工作負荷引镊。
借助于消息隊列,系統(tǒng)的不同部分可以相互通信并異步執(zhí)行處理操作篮条。消息隊列提供了一個臨時存儲消息的輕量級緩沖區(qū)弟头,以及允許軟件組件連接到隊列以發(fā)送和接收消息的終端節(jié)點。這些消息通常較小兑燥,可以是請求亮瓷、恢復(fù)琴拧、錯誤消息降瞳、明文信息等。
消息隊列有什么特性呢蚓胸?
- 業(yè)務(wù)無關(guān)
一個具有普適性質(zhì)的消息隊列組件不需要考慮上層的業(yè)務(wù)模型挣饥,只做好消息的分發(fā)就可以了,上層業(yè)務(wù)的不同模塊反而需要依賴消息隊列所定義的規(guī)范進行通信沛膳。 - FIFO 先進先出
先投遞先到達是消息隊列和buffer
的本質(zhì)區(qū)別 - 容災(zāi)
對于普適的消息隊列組件而言扔枫,節(jié)點的動態(tài)刪減和消息的持久化都是支持容災(zāi)能力的重要基本特性。 - 性能
消息隊列的吞吐量上去了整個系統(tǒng)的內(nèi)部通信效率也會有提高
推拉模型
消息隊列的推拉模型
-
push
推消息模型
消息生產(chǎn)者將消息發(fā)送給消息中間件锹安,消息中間件再將消息推送給消費者短荐。
push
模型最大的問題是慢消費,也就是說消費者消費速度比生產(chǎn)者的生產(chǎn)速度慢叹哭,將會導(dǎo)致在broker
的堆積忍宋,如果這些消息是有用且無法丟掉的就會一直在broker
中保存,而且broker
會不斷地給消費者推送消息风罩,消費者reject
或error
之后可能會來回推送糠排。
-
pull
拉消息模型
消費者請求消息中間件并接收消息,消費者從消息中間件拉取消息超升。
pull
模型中消費者可以按需消費入宦,不用擔心自己處理不了的信息來騷擾自己,broker
堆積消息也會相對簡單室琢,無需記錄每一個要發(fā)送消息的狀態(tài)乾闰,只需要維護所有消息的隊列和偏移量即可。由于主動權(quán)在消費方盈滴,消費方無法確切的決定何時去拉去最新的消息涯肩,如果一次拉取了消息還可以繼續(xù)去拉,如果沒有拉取則需要等待一段時間重新拉取。
pull
模型最大的問題是消息延遲和忙等宽菜,業(yè)界比較成熟的做法是從短時間開始(不會對broker
有太大負擔)谣膳,然后指數(shù)級增長等待。比如開始等5毫秒铅乡,然后10毫秒继谚,然后20毫秒...,直到有消息到來阵幸,然后再回到5毫秒花履。
優(yōu)缺點
為什么需要消息隊列呢?
當系統(tǒng)中出現(xiàn)生產(chǎn)和消費的速度或穩(wěn)定性等因素不一致的時候就需要消息隊列挚赊,作為抽象層诡壁,彌合雙方的差異。
關(guān)鍵在于:解耦荠割、異步妹卿、削峰
在高并發(fā)環(huán)境下,由于來不及同步處理蔑鹦,請求往往會發(fā)生阻塞夺克。
例如:大量的插入、更新語句類的請求同時達到數(shù)據(jù)庫將會直接導(dǎo)致無數(shù)的行鎖表鎖嚎朽,最后甚至會因為請求堆積過多從而觸發(fā)連接數(shù)不足的錯誤铺纽。通過使用消息隊列可以異步的處理請求從而緩解系統(tǒng)的壓力。
通過異步處理可以提高系統(tǒng)性能
例如:在不使用消息隊列的時候哟忍,用戶請求的數(shù)據(jù)將會直接寫入數(shù)據(jù)庫狡门,在高并發(fā)情況下數(shù)據(jù)庫壓力倍增,響應(yīng)速度變慢锅很。使用消息隊列之后其馏,用戶請求數(shù)據(jù)發(fā)送給消息隊列后立即返回,再由消息隊列的消費者進程從消息隊列中獲取數(shù)據(jù)粗蔚,異步寫入數(shù)據(jù)庫尝偎。由于消息隊列服務(wù)器處理速度快于數(shù)據(jù)庫,因此響應(yīng)速度得到大幅改善鹏控。
通過異步處理將短時間高并發(fā)產(chǎn)生的事務(wù)消息存儲在消息隊列中從而削平高峰期的并發(fā)事務(wù)致扯。
消息隊列的特點是什么?
- 消息隊列把請求的壓力保存一下然后逐漸釋放出來当辐,按照自己的節(jié)奏來處理抖僵。
- 消息隊列引入了新的節(jié)點,系統(tǒng)的可靠性會受到消息隊列節(jié)點的影響缘揪。
- 消息隊列是異步單向的消息耍群,發(fā)送消息被設(shè)計成無需等待處理的完成义桂。
消息隊列有什么缺點呢?
- 系統(tǒng)可用性降低
- 系統(tǒng)復(fù)雜性增加
- 數(shù)據(jù)一致性問題
應(yīng)用場景
消息隊列有哪些應(yīng)用場景蹈垢?當需要使用消息隊列時首先需要考慮它的必要性慷吊,可以使用消息隊列的場景很多,最常用的如應(yīng)用程序松耦合曹抬、異步處理模式溉瓶、發(fā)布與訂閱、最終一致性谤民、錯峰流控堰酿、日志緩沖等。反知张足,如果需要強一致性触创,關(guān)注業(yè)務(wù)邏輯的處理結(jié)構(gòu)則使用RPC
顯得更為合適。
應(yīng)用耦合
應(yīng)用系統(tǒng)之間解耦和主要體現(xiàn)在發(fā)送者和接收者不必了解對象只需要確認消息为牍,另外發(fā)送者接收者不必同時在線哼绑。
多應(yīng)用之間通過消息隊列對同一消息進行處理,避免調(diào)用接口失敗導(dǎo)致整個過程失敗吵聪。
例如:用戶下單后訂單系統(tǒng)需要通知庫存系統(tǒng)凌那,傳統(tǒng)的做法是訂單系統(tǒng)調(diào)用庫存系統(tǒng)的接口,缺點在于如果庫存系統(tǒng)無法訪問訂單減庫存將失敗吟逝,從而導(dǎo)致訂單失敗。
問題的根源在于訂單系統(tǒng)與庫存系統(tǒng)緊密耦合赦肋,如何解決這個問題呢块攒?引入消息隊列,訂單系統(tǒng)在用戶下單后完成持久化處理佃乘,并將消息寫入消息隊列然后返回下單成功囱井。庫存系統(tǒng)訂閱下單消息,采用拉/推的方式喧锦,獲取下單信息腰根,然后根據(jù)下單信息進行庫存操作浸卦。
如果在下單時庫存系統(tǒng)不能正常使用,也不會影響正常下單住练。因為下單后訂單系統(tǒng)寫入消息隊列后就不再關(guān)系其他的后續(xù)操作了,因此實現(xiàn)了訂單系統(tǒng)與庫存系統(tǒng)的應(yīng)用解耦愁拭。
異步處理
異步處理的主要目的在于:非核心流程異步化讲逛,減少系統(tǒng)響應(yīng)時間,提高吞吐量
消息發(fā)送者可以發(fā)送一個消息而無需等待響應(yīng)岭埠,消息發(fā)送者將消息發(fā)送到一條虛擬的通道(主題或隊列)上盏混,消息接收者則訂閱或監(jiān)聽該通道蔚鸥。一條消息可能最終轉(zhuǎn)發(fā)給一個或多個消息接收者,這些接收者都無需對消息發(fā)送者做出同步回應(yīng)许赃,整個過程都是異步的止喷。
多應(yīng)用對消息隊列中同一消息進行處理,應(yīng)用間并發(fā)處理消息混聊,相比串行處理能減少處理時間启盛。
例如:用戶使用應(yīng)用進行注冊,系統(tǒng)需發(fā)送注冊郵件并驗證短信技羔。
對注冊的處理有兩種方式:串行僵闯、并行
-
串行方式:用戶注冊信息寫入數(shù)據(jù)庫后,先發(fā)送注冊郵件藤滥,再發(fā)送驗證短信鳖粟,最終發(fā)送短信驗證碼給客戶端。
并行方式:用戶注冊信息寫入數(shù)據(jù)庫后拙绊,同時發(fā)送短信和發(fā)送郵件并行處理向图。發(fā)短信和發(fā)郵件處理完畢后返回給客戶端。
假如三個業(yè)務(wù)節(jié)點每個的使用均為50毫秒标沪,不考慮網(wǎng)絡(luò)等其他開銷榄攀,那么串行方式的時間會是150毫秒,并行的方式可能是100毫秒金句。由于CPU在單位時間內(nèi)處理請求數(shù)量是一定的檩赢,假如CPU在1秒內(nèi)吞吐量是100次,則串行方式下1秒內(nèi)的CPU可處理的請求是100 * 10 / 150 約7次违寞,并行方式處理的請求數(shù)量是100 * 10 / 100約10次贞瞒。
使用傳統(tǒng)方式系統(tǒng)的性能(并發(fā)量、吞吐量趁曼、響應(yīng)時間等)存在瓶頸军浆,應(yīng)該如何解決這個問題呢?
引入消息隊列挡闰,用戶注冊信息寫入數(shù)據(jù)庫后乒融,寫入消息隊列后立即返回給客戶端∩忝酰總的響應(yīng)時間依賴于寫入消息隊列的時間赞季,由于寫入消息隊列的時間本身很快基本可忽略不計。
按照之前約定射众,用戶的響應(yīng)時間相當于是注冊信息寫入數(shù)據(jù)庫的時間碟摆,也就是50毫秒。系統(tǒng)的吞吐量提高到100 * 10 / 50約每秒20QPS叨橱,相比串行提高了3倍典蜕,相比并行提高了2倍断盛。
限流削峰
流量削峰是消息隊列常用場景,應(yīng)用于秒殺或搶購活動中愉舔,避免流量過大導(dǎo)致應(yīng)用系統(tǒng)性能掛掉的情況钢猛。
例如:購物網(wǎng)站開展秒殺活動,由于瞬間訪問量過大服務(wù)器接收過大會導(dǎo)致流量暴增轩缤,相關(guān)系統(tǒng)無法處理請求甚至崩潰命迈。加入消息隊列后,系統(tǒng)從消息隊列中取數(shù)據(jù)火的,相當于消息隊列做了一次緩沖壶愤。
這樣做的優(yōu)勢在于請求先寫入消息隊列而非由業(yè)務(wù)處理系統(tǒng)直接處理,做了一次緩沖馏鹤,極大地減少了業(yè)務(wù)處理系統(tǒng)的壓力征椒。其次,隊列長度是可以做限制的湃累,也就是說可以控制活動的人數(shù)勃救。
事實上,秒殺時后入隊的用戶無法秒殺到商品治力,這些請求可以直接被拋棄蒙秒,返回活動已經(jīng)結(jié)束或商品已經(jīng)售完等信息。消息隊列的長度一旦超過最大限制數(shù)量會直接拋棄用戶請求或跳轉(zhuǎn)到錯誤頁面宵统。秒殺業(yè)務(wù)根據(jù)消息隊列中的請求信息再做后續(xù)處理晕讲。
消息驅(qū)動的系統(tǒng)
消息驅(qū)動的系統(tǒng)分為消息隊列、消息生產(chǎn)者榜田、消息消費者益兄,生產(chǎn)者負責產(chǎn)生消息,消費者負責對消息進行處理箭券。
例如:用戶新上傳一批照片,人臉識別系統(tǒng)需要對用戶的所有照片進行聚類疑枯,聚類完畢后由對賬系統(tǒng)重新生成用戶的人臉索引以加快查詢辩块。這三個子系統(tǒng)間由消息隊列連接起來,前一階段的處理結(jié)果放入隊列中荆永,后一階段從隊列中獲取消息繼續(xù)處理废亭。這樣做的好處在于避免了直接調(diào)用下一個系統(tǒng)導(dǎo)致當前系統(tǒng)失敗。其次每個子系統(tǒng)對于消息的處理方式可以更為靈活具钥,可以選擇收到消息時就處理豆村,也可以劃分時間段按不同的處理速度進行處理。
確認機制
消息的確認機制是什么骂删?
消息的ACK(Ackownledge)
確認機制掌动,為了保證消息不丟失四啰,消息隊列提供了消息Acknowledge
即ACK
機制,當消費者Consumer
確認消息已經(jīng)被消費處理后會發(fā)送一個ACK
給消息隊列粗恢,此時消息隊列便可以刪除這個消息柑晒。如果消費者Consumer
宕機或關(guān)閉,沒有發(fā)送ACK
眷射,消息隊列將認為這個消息沒有被處理匙赞,會見這個消息重新發(fā)送給其他的消費者重新消費處理。
傳輸模式
消息隊列有哪幾種傳輸模式呢妖碉?
消息隊列的傳輸模式可以分為兩種涌庭,分別是點對點模式(point to point,queue
)和發(fā)布/訂閱模式(publish/subscribe欧宜,topic
)
點對點模式
點對點模型用于消息生產(chǎn)者和消息消費者之間點到點的通信坐榆,消息生產(chǎn)者將消息發(fā)送到由某個名字標識的特定消費者,這個名字實際上對應(yīng)消費服務(wù)中的一個隊列鱼鸠,在消息傳遞給消費者之前它被存儲在這個隊列中猛拴,隊列消息可以存放在內(nèi)存也可以持久化,以保證在消息服務(wù)出現(xiàn)故障時仍然能夠傳遞消息蚀狰。
消息生產(chǎn)者向一個特定的隊列發(fā)送消息愉昆,消息消費者從該隊列中接收消息,消息生產(chǎn)者和消費者可以不同時處于運行狀態(tài)麻蹋,每一個成功處理的消息都是由消息消費者簽收確認(Acknowledge
跛溉,ACK
)。
點對點模式下包含三個角色:消息隊列扮授、發(fā)送者(生產(chǎn)者)芳室、接收者(消費者)
消息發(fā)送者生產(chǎn)消息并發(fā)送到消息隊列中,然后消息接收者從隊列中取出并消費刹勃。消息被消費后隊列中不再有存儲堪侯,所以消息接收者不可能消費到已經(jīng)被消費的消息。
點對點模式的特點在于每個消息只有一個接收者(消費者)荔仁,也就是說消息一旦被消費就不會在消息隊列中存在伍宦。另外,發(fā)送者和接收者之間是沒有依賴性的乏梁,發(fā)送者發(fā)送消息之后次洼,不管有沒有接收者在運行,都不會影響到發(fā)送者下次發(fā)送消息遇骑。再者卖毁,接收者在成功接收消息之后需向隊列應(yīng)答成功,以便消息隊列刪除當前接收的消息落萎。
傳統(tǒng)的點對點消息中間件通常是由消息隊列服務(wù)亥啦、消息傳遞服務(wù)炭剪、消息隊列、消息應(yīng)用程序接口API組成禁悠。
其特點在于每個消息只用一個消費者念祭,發(fā)送者和接收者之間沒有時間依賴,接收者確認消息接收和處理成功碍侦。
發(fā)布/訂閱模式
發(fā)布/訂閱(pub/sub
)模式中包含三個角色:角色主題(topic
)粱坤、發(fā)布者(publisher
)、訂閱者(subscriber
)
發(fā)布/訂閱模型支持先一個特定的消息主題生產(chǎn)消息瓷产,零個或多個訂閱者可能對接收來自特定消息主題的消息感興趣站玄。這種模型下,發(fā)布者和訂閱者彼此不知道對象濒旦,就好比是匿名公告板株旷。
發(fā)布/訂閱模式可以被概況為多個消費者可以獲得消息,在發(fā)布者和訂閱者之間存在時間依賴性尔邓。發(fā)布者需要建立一個訂閱(subscription
)以便能夠被消費者訂閱晾剖,訂閱者必須保證持續(xù)的活動狀態(tài)并接收消息。
發(fā)布訂閱消息模型中梯嗽,支持向一個特定的主題發(fā)布消息齿尽,零個或多個訂閱者接收來自這個消息主題的消息。在這種模型下灯节,發(fā)布者和訂閱者彼此不知道對象循头。發(fā)布者將消息發(fā)送到角色主題中,系統(tǒng)將這些消息傳遞給多個訂閱者炎疆。發(fā)布/訂閱模型的特點在于每個消息可以有多個訂閱者卡骂,發(fā)布者和訂閱者之間有時間上的依賴性。針對某個主題的訂閱者必須創(chuàng)建一個訂閱之后才能消費發(fā)布者的消息形入。為了消費消息全跨,訂閱者需要提前訂閱該角色主題并保持在線運行。在這種情況下亿遂,在訂閱者未連接時螟蒸,發(fā)布的消息將在訂閱者重新連接時重新發(fā)布。
發(fā)布訂閱的特點在于每個消息可以有多個訂閱者崩掘,客戶端只有訂閱后才能接受到消息。發(fā)布者和訂閱者之間有時間依賴性少办,接收者和發(fā)布者只有建立訂閱關(guān)系后才能接收到消息苞慢。
訂閱可分為持久訂閱和非持久訂閱,持久訂閱表示訂閱關(guān)系建立后消息就不會消失英妓,不管訂閱者是否在線挽放。非持久訂閱表示訂閱者為了接收消息绍赛,必須一直在線,當只有一個訂閱者時可視為點對點模式辑畦。
MQ與RPC
消息隊列與RPC的異同點是什么呢吗蚌?
RPC遠程過程調(diào)用的結(jié)構(gòu)是消費者(Consumer)調(diào)用提供者(Provider)所提供的服務(wù),消息隊列的結(jié)構(gòu)是發(fā)送者(Sender)將消息發(fā)送給隊列(Queue)纯出,接收者(Receiver)從消息隊列中取出消息進行處理蚯妇。
架構(gòu)上,遠程過程調(diào)用RPC和消息隊列Message Queue的差異點在于消息隊列有一個中間節(jié)點Message Queue(broker)暂筝,可以將消息緩存箩言。
RPC遠程過程調(diào)用的特點在于同步調(diào)用,適用于要等待返回結(jié)果或處理結(jié)果的場景焕襟,RPC也支持異步調(diào)用陨收。由于RPC需要等待結(jié)果,所以消費者Consumer(Client)會有線程上的消耗鸵赖。如果以異步的方式使用RPC务漩,消費者Consumer(Client)是沒有線程消耗的,但是不能做到像消息一樣被緩存它褪,壓力會直接傳導(dǎo)到服務(wù)提供者Provider那里饵骨。
RPC的使用場景主要包括
- 希望同步得到結(jié)果的場景
- RPC操作基于接口使用簡單
- 不希望發(fā)送端受限于處理端的速度時可以使用消息隊列
重復(fù)消費
如何保證消息不會被重復(fù)消費呢?如何保證消息隊列的冪等性呢列赎?
首先要明白為什么會造成重復(fù)消費宏悦?其實無論是哪種消息隊列,造成重復(fù)消費的原因都是類似的包吝。正常情況下饼煞,消費者在消費消息完畢后會發(fā)送一個確認消息給消息隊列,消息隊列收到后就知道該消息被消費了诗越,然后會將消息從消息隊列中刪除砖瞧。只是不同的消息隊列發(fā)出的確認消息形式不同。那造成重復(fù)消費的原因是什么呢嚷狞?就是因為網(wǎng)絡(luò)傳輸?shù)裙收峡榇伲_認消息沒有傳遞到消息隊列,導(dǎo)致消息隊列不知道自己已經(jīng)消費過該消息床未,會再次將消息分發(fā)給其他消費者竭翠。
如何解決這個問題呢?需針對不同的業(yè)務(wù)場景來解決:
- 如果是拿消息做數(shù)據(jù)庫的插入操作薇搁,那么給這個消息做一個唯一主鍵斋扰,出現(xiàn)重復(fù)消費將導(dǎo)致主鍵沖突,即可避免數(shù)據(jù)庫出現(xiàn)臟數(shù)據(jù)。
- 如果是拿消息做Redis的集合操作則不用解決传货,因為集合操作本身就是冪等操作屎鳍。
- 如果兩種情況都不行,準備一個第三方截止來做消費記錄问裕。以Redis為例給消息分配一個全局ID逮壁,只要消費過該消息將
<id, message>
以鍵值對形式存入Redis,消費者消費之前先去Redis中查詢是否有消費記錄即可粮宛。
可靠性
如何保證消費的可靠性傳輸呢窥淆?
對于可靠性傳輸,消息隊列需要從三個角度來分析:生產(chǎn)者弄丟數(shù)據(jù)窟勃、消息隊列弄丟數(shù)據(jù)祖乳、消費者弄丟數(shù)據(jù)
順序性
如何保證消息的順序性呢?
隊列本身是一種先進先出的數(shù)據(jù)結(jié)構(gòu)秉氧,所以消費消息時也是安裝順序來消費的眷昆。但也會出現(xiàn)消息被消費的順序錯誤的情況,例如某個消息消費失敗或一個隊列多個消費者時也會導(dǎo)致消息被消費的順序錯誤汁咏。
消息堆積
如何處理消息堆積的問題呢亚斋?處理消息堆積的方法就是將它保存下來,只是這個存儲可以做成很多種方式攘滩,如存儲在內(nèi)存中帅刊,存儲在分布式KV
里,存儲在磁盤漂问,存儲在數(shù)據(jù)庫等赖瞒。歸根結(jié)底主要有持久化和非持久化兩種方式。持久化的方式能更大程度地保證消息的可靠性蚤假,如斷電等不可抗拒的外力因素栏饮,理論上能承載更大限度的消息堆積。但并不是每種消息都需要持久化存儲的磷仰,很多消息對于投遞性能的要求大于可靠性的要求袍嬉,且數(shù)據(jù)極大如日志。這個時候消息不落地直接暫存內(nèi)存灶平,嘗試幾次allover
最終投遞出去也未嘗不可伺通。
未完待續(xù)...