一、三大應(yīng)用場景(優(yōu)點)
解耦汉嗽、異步欲逃、削峰
- 解耦:只需要將消息寫入消息隊列,需要消息的去消息隊列中訂閱就好
- 異步:一些非必要的邏輯可以采用異步來完成诊胞,從而提升響應(yīng)速度
- 削峰:某個時間段并發(fā)量特別大的時候可以將消息發(fā)送到消息隊列中暖夭,然后從消息隊列中慢慢拉取進(jìn)行消費(fèi)
解耦
傳統(tǒng)模式:
傳統(tǒng)模式缺點:
系統(tǒng)間耦合性太強(qiáng),如上圖所示撵孤,系統(tǒng)A在代碼中直接調(diào)用系統(tǒng)B和系統(tǒng)C迈着,如果將來系統(tǒng)D要接入,系統(tǒng)A還要修改代碼邪码,過于麻煩裕菠!
中間件模式優(yōu)點:
將消息寫入消息隊列,要消息的系統(tǒng)自己從消息隊列中訂閱闭专,從而系統(tǒng)A不再需要做任何修改
異步
傳統(tǒng)模式:
一些非必要的業(yè)務(wù)邏輯以同步的方式運(yùn)行奴潘,太耗費(fèi)時間。
中間件模式:
中間件模式的優(yōu)點:
將消息寫入消息隊列影钉,非必要的業(yè)務(wù)邏輯以異步的方式運(yùn)行画髓,加快響應(yīng)速度
削峰
傳統(tǒng)模式:
傳統(tǒng)模式缺點:
并發(fā)量大的時候,所有的請求直接懟到數(shù)據(jù)庫平委,造成數(shù)據(jù)庫連接異常
中間件模式:
中間件模式的優(yōu)點:
系統(tǒng)A慢慢的按照數(shù)據(jù)庫能處理的并發(fā)量奈虾,從消息隊列中慢慢拉取消息。在生產(chǎn)中廉赔,這個短暫的高峰期積壓是允許的肉微。
二、消息隊列的缺點
- 系統(tǒng)可用性降低:如系統(tǒng)原本運(yùn)行的好好的蜡塌,加入消息隊列后一旦消息隊列掛掉碉纳,系統(tǒng)直接就over了
- 增加系統(tǒng)的復(fù)雜度:采用消息隊列后要考慮好多問題,如一致性馏艾、如何保證消息不被重復(fù)消費(fèi)劳曹、消息的可靠傳輸
三、消息隊列的選型
1琅摩、ActiveMQ:更新比較慢厚者、java開發(fā)的、萬級吞吐量
2迫吐、RabbitMQ:相對ActiveMQ來說更新較快、erlang語言開發(fā)(erlang語言天生具有高并發(fā)的特效账忘,但是熟悉erlang的人相對較少志膀,好在社區(qū)比較活躍)
3熙宇、RocketMQ:支持分布式架構(gòu)、Java語言開發(fā)可以定制化開發(fā)
4溉浙、Kafka:支持分布式架構(gòu)烫止、吞吐量十萬級
使用場景:
在我們的電商中,在發(fā)送短信和商品上下架的時候使用了rebbitMQ
四戳稽、保證消息隊列的高可用
分析:在第二點說過了馆蠕,引入消息隊列后,系統(tǒng)可用性下降惊奇,在生產(chǎn)中互躬,沒人使用單機(jī)模式的消息隊列,因為作為一個合格的程序員颂郎,應(yīng)該對消息隊列的高可用有很深刻的了解吼渡。
如果面試時問如何保證高可用?你的回答只是表明自己只會訂閱和發(fā)布消息乓序,面試官就會懷疑你是不是只是自己搭著玩寺酪,沒有生產(chǎn)使用過。
以RCOKETMQ為例替劈,它的集群就有多master模式寄雀、多master多slave異步復(fù)制模式、多master多slave同步雙寫模式陨献。多master多slave模式部署架構(gòu)圖:
其實博主第一眼看到這個圖盒犹,就覺得和kafka好像,只是NameServer集群湿故,在kafka中用zookeeper代替阿趁,都是用來保存和發(fā)現(xiàn)master和slave用的,通信過程如下:
Producer與NameServer集群中的其中一個節(jié)點(隨機(jī))建立長連接坛猪,定期從NameServer獲取Topic路由信息脖阵,并向提供Topic服務(wù)的Broker Master和Slave建立長連接,即可以從Broker Master訂閱消息墅茉,也可以從Broker Slave訂閱消息命黔。
那么kafka呢,為了對比說明直接上kafka拓補(bǔ)結(jié)構(gòu):
如上圖所示就斤,一個典型的Kafka集群中包含若干Producer(可以是web前端產(chǎn)生的page view悍募,或者是服務(wù)器日志,系統(tǒng)cpu洋机,memory等)坠宴,若干broker(Kafka支持水平擴(kuò)展,一般broker數(shù)量越多绷旗,集群吞吐率越高)喜鼓,若干Consumer Group副砍,以及一個Zookeeper集群。Kafka通過broker庄岖,Consumer使用pull模式從broker訂閱并消費(fèi)消息豁翎。
至于rabbitMQ也有普通集群和鏡像集群模式,自行去了解隅忿,比較簡單心剥。
五、如何保證不會重復(fù)消費(fèi)
1背桐、RabbitMQ會發(fā)生一個ACK確認(rèn)消息
2优烧、RocketMQ會返回一個CONSUME_SUCCESS成功標(biāo)志
3、Kafka每條消息會有一個offset牢撼,在消費(fèi)后需要提交offset匙隔,讓消息隊列知道已經(jīng)消費(fèi)
六、如何保證消息的可靠傳輸
分析:在使用消息隊列過程中熏版,應(yīng)該做到消息不能多消費(fèi)纷责,也不能少消費(fèi)。如果無法做到可靠性傳輸撼短,可能給公司帶來千萬級別的財產(chǎn)損失再膳。同樣的,如果可靠性傳輸在使用過程中曲横,沒有考慮到喂柒,這不是給公司挖坑么。還是那句話禾嫉,認(rèn)真對待每一個項目灾杰,不要給公司挖坑。
三個角度:生產(chǎn)者弄丟數(shù)據(jù)熙参、消息隊列弄丟數(shù)據(jù)艳吠、消費(fèi)者弄丟數(shù)據(jù)
RabbitMQ
RabbitMQ提供transaction和confirm來保證生產(chǎn)者不丟消息
1.1 transaction機(jī)制就是說在發(fā)送消息的時候開啟事務(wù)<channel.txSelect()>,然后再發(fā)送消息孽椰,若發(fā)送過程中發(fā)生異常則會滾<channel.txRollback()>昭娩,成功則提交<channel.txCommit()>,缺點就是吞吐量下降
1.2 confirm模式生產(chǎn)上用的居多黍匾,一旦channel進(jìn)入confirm模式栏渺,所有在該信道上的消息都會被指派一個唯一ID(從1開始),一旦消息投遞到所匹配的隊列都锐涯,RabbitMQ會發(fā)送一個Ack(包含消息的唯一ID)給生產(chǎn)者磕诊,這就知道已經(jīng)消費(fèi)處理了,如果RabbitMQ沒有處理這條消息,則會發(fā)送一個Nack消息給你秀仲,這就知道消費(fèi)失敗融痛,然后就可以重試了。RabbitMQ消息隊列丟數(shù)據(jù)
2.1 處理消息隊列丟數(shù)據(jù)的情況神僵,一般是開啟持久化磁盤的配置。這個持久化配置可以和confirm機(jī)制配合使用覆劈,你可以在消息持久化磁盤后保礼,再給生產(chǎn)者發(fā)送一個Ack信號。這樣责语,如果消息持久化磁盤之前炮障,rabbitMQ陣亡了,那么生產(chǎn)者收不到Ack信號坤候,生產(chǎn)者會自動重發(fā)胁赢。
2.11 那么如何持久化呢,這里順便說一下吧白筹,其實也很容易智末,就下面兩步
2.11 1. 將queue的持久化標(biāo)識durable設(shè)置為true,則代表是一個持久的隊列
2.11 2. 發(fā)送消息的時候?qū)eliveryMode=2
這樣設(shè)置以后,rabbitMQ就算掛了徒河,重啟后也能恢復(fù)數(shù)據(jù)RabbitMQ消費(fèi)者丟數(shù)據(jù)
3.1 消費(fèi)者丟數(shù)據(jù)一般是因為采用了自動確認(rèn)消息模式(采用手動提交即可)
Kafka
這里先引用一張kafka replication的效果圖
Producer在發(fā)布消息到某個Partition時系馆,先通過zookeeper找到該partition的leader,然后無論該topic的replication factor為多少(也即該partition有多少個replication)顽照,Producer只將該消息發(fā)送到該P(yáng)artition的leader由蘑,leader會將該消息寫入本地log,每個Follower都從Leader中pull數(shù)據(jù)代兵。
Kafka生產(chǎn)丟失數(shù)據(jù)
4.1 在kafka生產(chǎn)中尼酿,基本都有一個leader和多個follwer。follwer會去同步leader的信息植影。因此裳擎,為了避免生產(chǎn)者丟數(shù)據(jù),做如下兩點配置
第一個配置要在producer端設(shè)置acks=all何乎。這個配置保證了句惯,follwer同步完成后,才認(rèn)為消息發(fā)送成功支救。
在producer端設(shè)置retries=MAX抢野,一旦寫入失敗,這無限重試Kafka消息隊列丟數(shù)據(jù)
5.1 針對消息隊列丟數(shù)據(jù)的情況各墨,無外乎就是指孤,數(shù)據(jù)還沒同步,leader就掛了,這時zookpeer會將其他的follwer切換為leader,那數(shù)據(jù)就丟失了恃轩。針對這種情況结洼,應(yīng)該做兩個配置。
replication.factor參數(shù)叉跛,這個值必須大于1松忍,即要求每個partition必須有至少2個副本
min.insync.replicas參數(shù),這個值必須大于1筷厘,這個是要求一個leader至少感知到有至少一個follower還跟自己保持聯(lián)系鸣峭。這兩個配置加上上面生產(chǎn)者的配置聯(lián)合起來用,基本可確保kafka不丟數(shù)據(jù)Kafka消費(fèi)者丟數(shù)據(jù)
6.1 這種情況一般是自動提交了offset酥艳,然后你處理程序過程中掛了摊溶。kafka以為你處理好了。解決方案也很簡單充石,改成手動提交即可莫换。
6.2 offset介紹:
6.21 offset:指的是kafka的topic中的每個消費(fèi)組消費(fèi)的下標(biāo)。簡單的來說就是一條消息對應(yīng)一個offset下標(biāo)骤铃,每次消費(fèi)數(shù)據(jù)的時候如果提交offset拉岁,那么下次消費(fèi)就會從提交的offset加一那里開始消費(fèi)。
比如一個topic中有100條數(shù)據(jù)劲厌,我消費(fèi)了50條并且提交了膛薛,那么此時的kafka服務(wù)端記錄提交的offset就是49(offset從0開始),那么下次消費(fèi)的時候offset就從50開始消費(fèi)补鼻。