首先整理這個文章是因為我正好有機會實戰了一下rocketmq,阿里巴巴的一個開源消息中間件。所以就與以往中rabbitmq進行小小的比較一下。這里主線的根據常見面試問題進行整理。
一.消息隊列常用的場景
1.削峰
例如我們做得考試系統中,用戶通過人臉識別登錄系統,考慮到考試系統的特殊性,三萬名考生參加考試,需要記錄人臉識別登錄照片。從考試完結果上看,用戶最大並發數在4000,於是我們采用rocketMq來進行異步消費用戶人臉識別圖片,當時統計rocketMq每秒1000消費消息。及時反饋了考生人臉識別登錄成功,對數據庫寫操作也起到很大的緩沖功能。
2.解耦
如常用ABCD系統中,BCD系統都需要從A系統中調用接口返回數據,這時候突然來了E系統,也需要A系統數,又或者C系統不想要用這個接口數據了,而且A系統還得考慮,如果BCD接收不到數據,接收失敗咋整之類的問題。
如果基於消息隊列,這些問題就迎刃而解了。
A系統直接把數據扔到Mq中,BCDE系統直接從Mq中消費,如果消費失敗,則重試消費。
3.異步
比如下訂單系統中,會調用庫存系統,會調用倉庫系統,積分系統等,用戶訂單操作會直接返回給用戶信息,提示訂單完成,至於庫存減少,或者倉庫發貨又或者積分的增加等,都是異步完成。極大的提高用戶響應速度。
二.各種消息隊列優缺點
1.rabbitMq
rabbitMq 幾萬級數據量,基於erlang語言開發,因此響應速度快些,並且社區活躍度比較活躍,可視化界面。缺點就是數據吞吐量相對與小一些,並且是基於erlang語言開發,比較重的問題難以維護。
2.rocketMq
rocketMq幾十萬級別數據量,基於Java開發,應對了淘寶雙十一考驗,並且文檔十分的完善,擁有一些其他消息隊列不具備的高級特性,如定時推送,其他消息隊列是延遲推送,如rabbitMq通過設置expire字段設置延遲推送時間。又比如rocketmq實現分布式事務,比較可靠的。
3.kafka
kafka真正的大規模分布式消息隊列,提供的核心功能比較少。基於zookeeper實現的分布式消息訂閱。
三.消息隊列常使用的注意事項或者面試時候經常問道的功能點
1.如何保證系統的高可用
就rabbitMq而言,有鏡像模式概念,就是用戶在發送數據時候,發送到mq機器上,並且持久化磁盤,然后通過設置鏡像的queue,把數的持久化地址對應表同步到另外mq機器上。這種就有效防止一台mq掛了以后,另外的mq可以直接對外提供消費功能。
就rocketMq而言,分為多主集群結構,多主多備異步復制結構,多主多備同步復制結構。
2.如何保證消息不會丟失
就rabbitmq而言,從生產者,消費者,消息隊列角度分析。生產者而言,發送消息如果失敗,則定義重試次數,一般設置成五次。兩種解決方式1.通過設置事務,進行事務回滾重試。2.通過發送者確認模式開啟。
方式一:channel.waitForConfirms()普通發送方確認模式;
方式二:channel.waitForConfirmsOrDie()批量確認模式;
方式三:channel.addConfirmListener()異步監聽發送方確認模式;
就mq本身而言,需要做隊列的持久化到磁盤的操作。1.queque隊列的持久化,通過channel.queue_declare(queue
=
'hello'
, durable
=
True
);設置
2.設置消息的持久化,通過delivery_mode
=
2來進行設置。
mq消費者而言,開啟手動ACK模式,也就是需要真正的消費者入庫成功,才會進行消費成功的確認。
總結就是一句話:發送者確認模式開啟,消息持久化默認開啟,消費者消費開啟手動ack
rocketMq而言,生產者發送消息,生產者默認模式
rocketMq持久化方式中,消息持久化通過如下配置。
3.消費者冪等消費問題
感覺rabbitmq和rocketmq出現重復消費場景差不多
-
發送時消息重復
當一條消息已被成功發送到服務端並完成持久化,此時出現了網絡閃斷或者客戶端宕機,導致服務端對客戶端應答失敗。 如果此時生產者意識到消息發送失敗並嘗試再次發送消息,消費者后續會收到兩條內容相同並且 Message ID 也相同的消息。
-
投遞時消息重復
消息消費的場景下,消息已投遞到消費者並完成業務處理,當客戶端給服務端反饋應答的時候網絡閃斷。 為了保證消息至少被消費一次,消息隊列 RocketMQ 的服務端將在網絡恢復后再次嘗試投遞之前已被處理過的消息,消費者后續會收到兩條內容相同並且 Message ID 也相同的消息。
-
負載均衡時消息重復(包括但不限於網絡抖動、Broker 重啟以及訂閱方應用重啟)
當消息隊列 RocketMQ 的 Broker 或客戶端重啟、擴容或縮容時,會觸發 Rebalance,此時消費者可能會收到重復消息。
解決方式的話,通過messageId,作為數據庫業務主鍵,重復插入會報錯主鍵沖突問題。
或者通過redis唯一性,messageId作為key存入,去重重復的數據,在從redis中刷到數據庫里面。