【126期】??消息隊列面試連環(huán)炮
閱讀本文大概需要 12 分鐘。
目錄
項目里怎么樣使用 MQ 的?
為什么要使用消息隊列?
消息隊列有什么優(yōu)點(diǎn)和缺點(diǎn)?
kafka,activemq,rabbitmq,rocketmq 都有什么去唄?
如何保證消息隊列高可用?
如何保證消息不被重復(fù)消費(fèi)?
如何保證消息的可靠性傳輸?
如何保證消息的順序性?
寫一個消息隊列架構(gòu)設(shè)計?
消息隊列技術(shù)選型
解耦
異步
削峰
不用 MQ 系統(tǒng)耦合場景

使用 MQ 系統(tǒng)解耦場景

維護(hù)這個代碼,不需要考慮人家是否調(diào)用成功,失敗超時
如果新系統(tǒng)需要數(shù)據(jù),直接從 MQ 里消費(fèi)即可,如果某個系統(tǒng)不需要這條數(shù)據(jù)就取消對 MQ 消息的消費(fèi)即可。
不用 MQ 同步高延遲請求場景

使用 MQ 進(jìn)行異步化之后的接口性能優(yōu)化

沒有用 MQ 時高峰期系統(tǒng)被打死的場景

使用 MQ 進(jìn)行削峰的場景

算一筆賬,每秒積壓在 MQ 里消息有 3000 條,一分鐘就會積壓 18W 條消息,一個小時就會積壓 1000 萬條消息。等高峰期一過,差不多需要 1 個多小時就可以把 1000W 條積壓的消息給處理掉

系統(tǒng)可用性降低
系統(tǒng)復(fù)雜性變高
一致性問題
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么優(yōu)缺點(diǎn)

消息隊列高可用
RabbtitMQ 高可用
單機(jī)模式
普通集群模式(非高可用)

優(yōu)點(diǎn):可以多個機(jī)器消費(fèi)消息,可以提高消費(fèi)的吞吐量
缺點(diǎn):可能會在 rabbitmq 內(nèi)部產(chǎn)生大量的數(shù)據(jù)傳輸 ;可用性基本沒保障,queue 所在機(jī)器宕機(jī),就沒辦法消費(fèi)了
鏡像集群模式(高可用,非分布式)

Kafka 高可用架構(gòu)

Kafka 0.8版本之前是沒有 HA 機(jī)制的,任何一個 broker 宕機(jī)了,那么就缺失一部分?jǐn)?shù)據(jù)。
Kafka 0.8以后,提供了 HA 機(jī)制,就是 replica 副本機(jī)制。
leader和follower的同步機(jī)制:
消息隊列重復(fù)數(shù)據(jù)
Kafka 消費(fèi)端可能出現(xiàn)的重復(fù)消費(fèi)問題

重復(fù)消息原因:(主要發(fā)生在消費(fèi)者重啟后)
保證 MQ 重復(fù)消費(fèi)冪等性

思路:
拿數(shù)據(jù)要寫庫,首先檢查下主鍵,如果有數(shù)據(jù),則不插入,進(jìn)行一次update
如果是寫 redis,就沒問題,反正每次都是 set ,天然冪等性
生產(chǎn)者發(fā)送消息的時候帶上一個全局唯一的id,消費(fèi)者拿到消息后,先根據(jù)這個id去 redis里查一下,之前有沒消費(fèi)過,沒有消費(fèi)過就處理,并且寫入這個 id 到 redis,如果消費(fèi)過了,則不處理。
基于數(shù)據(jù)庫的唯一鍵
保證 MQ 消息不丟
RabbitMQ可能存在的數(shù)據(jù)丟失問題

生產(chǎn)者寫消息的過程中,消息都沒有到 rabbitmq,在網(wǎng)絡(luò)傳輸過程中就丟了。或者消息到了 rabbitmq,但是人家內(nèi)部出錯了沒保存下來。
RabbitMQ 接收到消息之后先暫存在主機(jī)的內(nèi)存里,結(jié)果消費(fèi)者還沒來得及消費(fèi),RabbitMQ自己掛掉了,就導(dǎo)致暫存在內(nèi)存里的數(shù)據(jù)給搞丟了。
消費(fèi)者消費(fèi)到了這個消費(fèi),但是還沒來得及處理,自己就掛掉了,RabbitMQ 以為這個消費(fèi)者已經(jīng)處理完了。
????channel.txSelect
try?{
????//發(fā)送消息
}?catch(Exception?e){
????channel.txRollback;
????//再次重試發(fā)送這條消息
}?
????channel.txCommit;
先把 channel 設(shè)置成 confirm 模式
發(fā)送一個消息到 rabbitmq
發(fā)送完消息后就不用管了
rabbitmq 如果接收到了這條消息,就會回調(diào)你生產(chǎn)者本地的一個接口,通知你說這條消息我已經(jīng)收到了
rabbitmq 如果在接收消息的時候報錯了,就會回調(diào)你的接口,告訴你這個消息接收失敗了,你可以再次重發(fā)。
public?void?ack(String?messageId){
}
public?void?nack(String?messageId){
????//再次重發(fā)一次這個消息
}
創(chuàng)建queue的時候?qū)⑵湓O(shè)置為持久化的,這樣就可以保證 rabbitmq持久化queue的元數(shù)據(jù),但是不會持久化queue里的數(shù)據(jù)
發(fā)送消息的時候?qū)?deliveryMode 設(shè)置為 2,將消息設(shè)置為持久化的,此時 rabbitmq就會將消息持久化到磁盤上去。必須同時設(shè)置 2 個持久化才行。
持久化可以跟生產(chǎn)者那邊的 confirm機(jī)制配合起來,只有消息被持久化到磁盤之后,才會通知生產(chǎn)者 ack了 ,所以哪怕是在持久化到磁盤之前 ,rabbitmq掛了,數(shù)據(jù)丟了,生產(chǎn)者收不到 ack,你也可以自己重發(fā)。
Kafka 可能存在的數(shù)據(jù)丟失問題

消費(fèi)端弄丟數(shù)據(jù)
Kafka 丟掉消息
解決方案:(保證 kafka broker端在 leader發(fā)生故障,或者leader切換時,數(shù)據(jù)不會丟)
給 topic設(shè)置 replication.factor ,這個值必須大于 1,保證每個 partition 必須至少有 2 個副本
在 kafka 服務(wù)端設(shè)置 min.insync.replicas 參數(shù),這個值必須大于 1,這個是要求一個leader至少感知到有至少一個follower還跟自己保持聯(lián)系,沒掉隊,這樣才能確保 leader掛了還有一個follower,保證至少一個 follower能和leader保持正常的數(shù)據(jù)同步。
在 producer 端設(shè)置 acks =all,這個是要求每條數(shù)據(jù),必須是寫入所有 replica 之后,才能認(rèn)為是寫成功了。否則會生產(chǎn)者會一直重試,此時設(shè)置 retries = MAX(很大的重試的值),要求一旦寫入失敗,就卡在這里(避免消息丟失)
kafka 生產(chǎn)者丟消息
消息隊列順序性
rabbitmq,一個queue,多個consumer,這不明顯亂了
kafka,一個topic,一個partition,一個consumer,內(nèi)部多線程,這不也亂了
RabbitMQ 消息順序錯亂

RabbitMQ 如何保證消息順序性

Kafka 消息順序錯亂

Kafka 保證消息順序性

消息隊列延遲以及過期失效
每次消費(fèi)之后都要寫 mysql,結(jié)果mysql掛了,消費(fèi)端 hang 不動了。
消費(fèi)者本地依賴的一個東西掛了,導(dǎo)致消費(fèi)者掛了。
長時間沒處理消費(fèi),導(dǎo)致 mq 寫滿了。
快速處理積壓的消息
先修復(fù) consumer 的問題,確保其恢復(fù)消費(fèi)速度,然后將現(xiàn)有的 consumer 都停掉
新建一個topic,partition是原來的 10 倍,臨時建立好原先 10 倍或者 20 倍的 queue 數(shù)量
然后寫一個臨時的分發(fā)數(shù)據(jù)的 consumer 程序,這個程序部署上去消費(fèi)積壓的數(shù)據(jù),消費(fèi)之后不做耗時的處理,直接均勻輪詢寫入臨時建立好的 10 倍數(shù)量的 queue
接著臨時征用 10 倍的機(jī)器來部署 consumer,每一批 consumer 消費(fèi)一個臨時 queue 的數(shù)據(jù)
這種做法相當(dāng) 于是臨時將 queue 資源和 consumer 資源擴(kuò)大 10 倍,以正常 10 倍速度
等快速消費(fèi)完積壓數(shù)據(jù)之后,恢復(fù)原先部署架構(gòu) ,重新用原先的 consumer機(jī)器消費(fèi)消息
這個時候開始寫程序,將丟失的那批 數(shù)據(jù)查出來,然后重新灌入mq里面,把白天丟的數(shù)據(jù)補(bǔ)回來。
如何設(shè)計消息隊列中間件架構(gòu)
mq要支持可伸縮性,快速擴(kuò)容。設(shè)計一個分布式的 MQ,broker->topic->partition,每個 partition 放一個機(jī)器,就存一部分?jǐn)?shù)據(jù)。如果現(xiàn)在資源不夠,給 topic 增加 partition ,然后做數(shù)據(jù)遷移,增加機(jī)器。
mq數(shù)據(jù)落磁盤,避免進(jìn)程掛了數(shù)據(jù)丟了,順序?qū)懀@樣就沒有磁盤隨機(jī)讀寫的尋址開銷,磁盤順序讀寫的性能是很高的,這個就是 kafka的思路。
mq高可用性。多副本->leader & follower-> broker 掛了重新選舉 leader 對外提供服務(wù)
支持?jǐn)?shù)據(jù) 0 丟失。
推薦閱讀:
【125期】舉例說明消息隊列應(yīng)用場景及ActiveMQ、RocketMQ、Kafka等的對比
【124期】面試官:談?wù)勎⒎?wù)的數(shù)據(jù)庫設(shè)計思路吧
微信掃描二維碼,關(guān)注我的公眾號
朕已閱?

