一、基本說明
面試官:你好
候選人:你好
(大家寒暄一下士聪。锦援。。)
(面試官在你的簡(jiǎn)歷上面看到了剥悟,呦灵寺,有個(gè)亮點(diǎn)曼库,就是你在項(xiàng)目里用過MQ,比如說你用過ActiveMQ)
面試官:你在系統(tǒng)里用過消息隊(duì)列嗎替久?(面試官在隨和的語氣中展開了面試)
候選人:用過的(此時(shí)感覺沒啥)
面試官:那你說一下你們?cè)陧?xiàng)目里是怎么用消息隊(duì)列的凉泄?
候選人:巴拉巴拉,我們啥啥系統(tǒng)發(fā)送個(gè)啥啥消息到隊(duì)列蚯根,別的系統(tǒng)來消費(fèi)啥啥的
(很多人在這里會(huì)進(jìn)入一個(gè)誤區(qū)后众,就是你僅僅就是知道以及回答你們是怎么用這個(gè)消息隊(duì)列的,用這個(gè)消息隊(duì)列來干了個(gè)什么事情颅拦?)
候選人:比如我們有個(gè)訂單系統(tǒng)蒂誉,訂單系統(tǒng)會(huì)每次下一個(gè)新的訂單的時(shí)候,就會(huì)發(fā)送時(shí)一條消息到ActiveMQ里面去距帅,后臺(tái)有個(gè)庫(kù)存系統(tǒng)負(fù)責(zé)獲取了消息然后更新庫(kù)存右锨。
面試官:那你們?yōu)槭裁词褂孟㈥?duì)列啊碌秸?
(你的訂單系統(tǒng)不發(fā)送消息到MQ绍移,直接訂單系統(tǒng)調(diào)用庫(kù)存系統(tǒng)一個(gè)接口,咔嚓一下讥电,直接就調(diào)用成功能了蹂窖,庫(kù)存就更新了)
候選人:額。恩敌。瞬测。(楞了一下,為什么纠炮?我沒怎么仔細(xì)想過啊月趟,老大讓用就用了),硬著頭皮胡言亂語了幾句
(面試官此時(shí)聽你楞了一下恢口,然后聽你胡言亂語了幾句孝宗,開始心里覺得有點(diǎn)兒那什么了,懷疑你之前就壓根兒沒思考過這問題)
面試官:那你說說用消息隊(duì)列都有什么優(yōu)點(diǎn)和缺點(diǎn)耕肩?
(面試官此時(shí)心里想的是因妇,你的MQ在項(xiàng)目里為啥要用?你沒考慮過看疗,那我稍微簡(jiǎn)單點(diǎn)兒沙峻,我問問你消息隊(duì)列你之前有沒有考慮過如果用的話,優(yōu)點(diǎn)和缺點(diǎn)分別是啥两芳?)
候選人:這個(gè)摔寨。。怖辆。(確實(shí)平時(shí)沒怎么考慮過這個(gè)問題啊是复。删顶。。胡言亂語了)
(面試官此時(shí)心里已經(jīng)更覺得你這哥兒們不行淑廊,平時(shí)都沒什么思考)
面試官:kafka逗余、activemq、rabbitmq季惩、rocketmq都有什么區(qū)別录粱?
(面試官問你這個(gè)問題,就是說画拾,繞過比較虛的話題啥繁,直接看看你對(duì)各種MQ中間件是否了解,是否做過功課青抛,是否做過調(diào)研)
候選人:我們就用過activemq旗闽,所以別的沒用過。蜜另。适室。區(qū)別,也不太清楚
(面試官此時(shí)卻是覺得你這哥兒們平時(shí)就是瞎用举瑰,根本就沒什么思考捣辆,覺得不行)
面試官:那你們是如何保證消息隊(duì)列的高可用啊嘶居?
候選人:這個(gè)罪帖。促煮。邮屁。我平時(shí)就是簡(jiǎn)單走api調(diào)用一下,不太清楚消息隊(duì)列怎么部署的菠齿。佑吝。。
面試官:如何保證消息不被重復(fù)消費(fèi)吧取芋忿?如何保證消費(fèi)的時(shí)候是冪等的啊疾棵?
候選人:啥戈钢?(mq不就是寫入和消費(fèi)就可以了,哪來這么多問題)
面試官:如何保證消息的可靠性傳輸笆嵌殉了?要是消息丟失了怎么辦啊拟枚?
候選人:我們沒怎么丟過消息啊薪铜。众弓。。
面試官:那如何保證消息的順序性隔箍?
候選人:順序性谓娃?什么意思?我為什么要保證消息的順序性蜒滩?
面試官:如何解決消息隊(duì)列的延時(shí)以及過期失效問題滨达?消息隊(duì)列滿了以后該怎么處理?有幾百萬消息持續(xù)積壓幾小時(shí)俯艰,說說怎么解決弦悉?
候選人:不是,我這平時(shí)沒遇到過這些問題啊蟆炊,就是簡(jiǎn)單用用稽莉,知道m(xù)q的一些功能
面試官:如果讓你寫一個(gè)消息隊(duì)列,該如何進(jìn)行架構(gòu)設(shè)計(jì)吧辍污秆?說一下你的思路
候選人:。昧甘。良拼。。充边。我還是走吧庸推。。浇冰。贬媒。
其實(shí)上面是一個(gè)非常典型的關(guān)于消息隊(duì)列的技術(shù)考察過程,好的面試官一定是從你做過的某一個(gè)點(diǎn)切入肘习,然后層層展開深入考察际乘,一個(gè)接一個(gè)問,直到把這個(gè)技術(shù)點(diǎn)刨根問底漂佩,問到最底層脖含。
如果沒有刻意的對(duì)這種面試方式鍛煉一下,出去面試碰到難一點(diǎn)的面試投蝉,大多會(huì)手忙腳亂养葵,基本面試以失敗為告終。
但是如果你把這些常見問題都掌握了瘩缆,哪怕是面試官?zèng)]問到你這么深入关拒,他問你一個(gè)消息隊(duì)列問題,你就自己給他說出自己的一整套見解,那么恭喜你夏醉,就是plus加分項(xiàng)了爽锥。
二、如何進(jìn)行消息隊(duì)列的技術(shù)選型
1畔柔、面試題
為什么使用消息隊(duì)列奥纫摹?消息隊(duì)列有什么優(yōu)點(diǎn)和缺點(diǎn)鞍胁痢腮考?kafka、activemq玄捕、rabbitmq踩蔚、rocketmq都有什么區(qū)別以及適合哪些場(chǎng)景?
2枚粘、面試官心理分析
其實(shí)面試官主要是想看看:
(1)第一馅闽,你知道不知道你們系統(tǒng)里為什么要用消息隊(duì)列這個(gè)東西?
好多人說自己項(xiàng)目里用了redis馍迄、mq福也,但是其實(shí)他并不知道自己為什么要用這個(gè)東西。其實(shí)說白了攀圈,就是為了用而用暴凑,或者是別人設(shè)計(jì)的架構(gòu),他從頭到尾沒思考過赘来。
沒有對(duì)自己的架構(gòu)問過為什么的人现喳,一定是平時(shí)沒有思考的人,面試官對(duì)這類候選人印象通常很不好犬辰。因?yàn)檫M(jìn)了團(tuán)隊(duì)擔(dān)心你就木頭木腦的干呆活兒嗦篱,不會(huì)自己思考。
(2)第二忧风,你既然用了消息隊(duì)列這個(gè)東西默色,你知道不知道用了有什么好處球凰?
系統(tǒng)中引入消息隊(duì)列之后會(huì)不會(huì)有什么壞處狮腿?你要是沒考慮過這個(gè),那你盲目弄個(gè)MQ進(jìn)系統(tǒng)里呕诉,后面出了問題你是不是就自己溜了給公司留坑缘厢?你要是沒考慮過引入一個(gè)技術(shù)可能存在的弊端和風(fēng)險(xiǎn),面試官把這類候選人招進(jìn)來了甩挫,基本可能就是挖坑型選手贴硫。
(3)第三,既然你用了MQ,可能是某一種MQ英遭,那么你當(dāng)時(shí)做沒做過調(diào)研凹浠ぁ?
你別傻乎乎的自己拍腦袋看個(gè)人喜好就瞎用了一個(gè)MQ挖诸,比如kafka汁尺。甚至都從沒調(diào)研過業(yè)界到底流行的MQ有哪幾種?每一個(gè)MQ的優(yōu)點(diǎn)和缺點(diǎn)是什么多律?每一個(gè)MQ沒有絕對(duì)的好壞痴突,但是就是看用在哪個(gè)場(chǎng)景可以揚(yáng)長(zhǎng)避短,利用其優(yōu)勢(shì)狼荞,規(guī)避其劣勢(shì)辽装。
如果是一個(gè)不考慮技術(shù)選型的候選人招進(jìn)了團(tuán)隊(duì),面試官交給他一個(gè)任務(wù)相味,去設(shè)計(jì)個(gè)什么系統(tǒng)拾积,他在里面用一些技術(shù),可能都沒考慮過選型丰涉,最后選的技術(shù)可能并不一定合適殷勘,一樣是留坑
3、面試題剖析
(1)為什么使用消息隊(duì)列拔袈А玲销?
其實(shí)就是問問你消息隊(duì)列都有哪些使用場(chǎng)景,然后你項(xiàng)目里具體是什么場(chǎng)景摘符,說說你在這個(gè)場(chǎng)景里用消息隊(duì)列是什么贤斜。
面試官問你這個(gè)問題,期望的一個(gè)回答是說逛裤,你們公司有個(gè)什么業(yè)務(wù)場(chǎng)景瘩绒,這個(gè)業(yè)務(wù)場(chǎng)景有個(gè)什么技術(shù)挑戰(zhàn),如果不用MQ可能會(huì)很麻煩带族,但是你現(xiàn)在用了MQ之后帶給了你很多的好處锁荔。
先說一下消息隊(duì)列的常見使用場(chǎng)景吧,其實(shí)場(chǎng)景有很多蝙砌,但是比較核心的有3個(gè):解耦阳堕、異步、削峰
1.解耦
A系統(tǒng)發(fā)送個(gè)數(shù)據(jù)到BCD三個(gè)系統(tǒng)择克,接口調(diào)用發(fā)送恬总,那如果E系統(tǒng)也要這個(gè)數(shù)據(jù)呢?那如果C系統(tǒng)現(xiàn)在不需要了呢肚邢?現(xiàn)在A系統(tǒng)又要發(fā)送第二種數(shù)據(jù)了呢壹堰?A系統(tǒng)負(fù)責(zé)人瀕臨崩潰中拭卿。趴久。猿挚。再來點(diǎn)更加崩潰的事兒,A系統(tǒng)要時(shí)時(shí)刻刻考慮BCDE四個(gè)系統(tǒng)如果掛了咋辦骤铃?我要不要重發(fā)谆焊?我要不要把消息存起來目木?頭發(fā)都沒了啊。懊渡。刽射。
你需要去考慮一下你負(fù)責(zé)的系統(tǒng)中是否有類似的場(chǎng)景,就是一個(gè)系統(tǒng)或者一個(gè)模塊剃执,調(diào)用了多個(gè)系統(tǒng)或者模塊誓禁,互相之間的調(diào)用很復(fù)雜,維護(hù)起來很麻煩肾档。但是其實(shí)這個(gè)調(diào)用是不需要直接同步調(diào)用接口的摹恰,如果用MQ給他異步化解耦,也是可以的怒见,你就需要去考慮在你的項(xiàng)目里俗慈,是不是可以運(yùn)用這個(gè)MQ去進(jìn)行系統(tǒng)的解耦。在簡(jiǎn)歷中體現(xiàn)出來這塊東西遣耍,用MQ作解耦闺阱。
2.異步
A系統(tǒng)接收一個(gè)請(qǐng)求,需要在自己本地寫庫(kù)舵变,還需要在BCD三個(gè)系統(tǒng)寫庫(kù)酣溃,自己本地寫庫(kù)要3ms,BCD三個(gè)系統(tǒng)分別寫庫(kù)要300ms纪隙、450ms赊豌、200ms。最終請(qǐng)求總延時(shí)是3 + 300 + 450 + 200 = 953ms绵咱,接近1s碘饼,用戶感覺搞個(gè)什么東西,慢死了悲伶。
但是此時(shí)如果采用消息隊(duì)列呢艾恼?
3.削峰
每天0點(diǎn)到11點(diǎn),A系統(tǒng)風(fēng)平浪靜拢切,每秒并發(fā)請(qǐng)求數(shù)量就100個(gè)蒂萎。結(jié)果每次一到11點(diǎn)~1點(diǎn),每秒并發(fā)請(qǐng)求數(shù)量突然會(huì)暴增到1萬條淮椰。但是系統(tǒng)最大的處理能力就只能是每秒鐘處理1000個(gè)請(qǐng)求啊。。主穗。尷尬了泻拦,系統(tǒng)會(huì)死。忽媒。争拐。
怎么辦?加機(jī)器晦雨?高峰也就一兩個(gè)小時(shí)架曹,平時(shí)流量根本就沒用這么大,為了倆小時(shí)的高峰期就加一堆機(jī)器顯然不劃算闹瞧。如果把高峰期的數(shù)據(jù)放到平時(shí)服務(wù)器空閑時(shí)間處理就好了绑雄,具體也就是要處理的數(shù)據(jù)先放到MQ中,然后消費(fèi)端慢慢處理就OK奥邮,高峰期數(shù)據(jù)可以先積壓到隊(duì)列中去万牺。
三、消息隊(duì)列的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn)上面已經(jīng)說了洽腺,就是在特殊場(chǎng)景下有其對(duì)應(yīng)的好處脚粟,解耦、異步蘸朋、削峰核无。缺點(diǎn)呢?顯而易見的藕坯。
系統(tǒng)可用性降低:系統(tǒng)引入的外部依賴越多厕宗,越容易掛掉,本來你就是A系統(tǒng)調(diào)用BCD三個(gè)系統(tǒng)的接口就好了堕担,人家ABCD四個(gè)系統(tǒng)好好的已慢,沒啥問題,你偏加個(gè)MQ進(jìn)來霹购,萬一MQ掛了咋整佑惠?MQ掛了,整套系統(tǒng)崩潰了齐疙,你不就完了么膜楷。
系統(tǒng)復(fù)雜性提高:硬生生加個(gè)MQ進(jìn)來,你怎么保證消息沒有重復(fù)消費(fèi)贞奋?怎么處理消息丟失的情況赌厅?怎么保證消息傳遞的順序性?頭大頭大轿塔,問題一大堆特愿,痛苦不已
一致性問題:A系統(tǒng)處理完了直接返回成功了仲墨,人家都以為你這個(gè)請(qǐng)求就成功了;但是問題是揍障,要是BCD三個(gè)系統(tǒng)那里目养,BD兩個(gè)系統(tǒng)寫庫(kù)成功了,結(jié)果C系統(tǒng)寫庫(kù)失敗了毒嫡,咋整癌蚁?你這數(shù)據(jù)就不一致了。
所以消息隊(duì)列實(shí)際是一種非常復(fù)雜的架構(gòu)兜畸,你引入它有很多好處努释,但是也得針對(duì)它帶來的壞處做各種額外的技術(shù)方案和架構(gòu)來規(guī)避掉,做好之后咬摇,你會(huì)發(fā)現(xiàn)伐蒂,媽呀,系統(tǒng)復(fù)雜度提升了一個(gè)數(shù)量級(jí)菲嘴,也許是復(fù)雜了10倍饿自。但是關(guān)鍵時(shí)刻,用龄坪,還是得用的昭雌。
四、幾個(gè)MQ的比較
kafka健田、activemq烛卧、rabbitmq、rocketmq都有什么優(yōu)點(diǎn)和缺點(diǎn)凹司帧总放?常見的MQ其實(shí)就這幾種,別的還有很多其他MQ好爬,但是比較冷門的局雄,那么就別多說了。作為一個(gè)碼農(nóng)存炮,你起碼得知道各種mq的優(yōu)點(diǎn)和缺點(diǎn)吧炬搭,咱們來畫個(gè)表格看看:
特性
ActiveMQ
RabbitMQ
RocketMQ
Kafka
單機(jī)吞吐量
萬級(jí),吞吐量比RocketMQ和Kafka要低了一個(gè)數(shù)量級(jí)穆桂。
萬級(jí)宫盔,吞吐量比RocketMQ和Kafka要低了一個(gè)數(shù)量級(jí)。
10萬級(jí)享完,RocketMQ也是可以支撐高吞吐的一種MQ灼芭。
10萬級(jí)別,這是kafka最大的優(yōu)點(diǎn)般又,就是吞吐量高彼绷;
一般配合大數(shù)據(jù)類的系統(tǒng)來進(jìn)行實(shí)時(shí)數(shù)據(jù)計(jì)算巍佑、日志采集等場(chǎng)景。
topic數(shù)量對(duì)吞吐量的影響
topic可以達(dá)到幾百苛预,幾千個(gè)的級(jí)別句狼,吞吐量會(huì)有較小幅度的下降笋熬;
這是RocketMQ的一大優(yōu)勢(shì)热某,在同等機(jī)器下,可以支撐大量的topic胳螟。
topic從幾十個(gè)到幾百個(gè)的時(shí)候昔馋,吞吐量會(huì)大幅度下降;
所以在同等機(jī)器下糖耸,kafka盡量保證topic數(shù)量不要過多秘遏。如果要支撐大規(guī)模topic,需要增加更多的機(jī)器資源嘉竟。
時(shí)效性
ms級(jí)
微秒級(jí)邦危,這是rabbitmq的一大特點(diǎn),延遲是最低的
ms級(jí)
延遲在ms級(jí)以內(nèi)
可用性
高舍扰,基于主從架構(gòu)實(shí)現(xiàn)高可用性
高倦蚪,基于主從架構(gòu)實(shí)現(xiàn)高可用性
非常高,分布式架構(gòu)
非常高边苹,kafka是分布式的陵且,一個(gè)數(shù)據(jù)多個(gè)副本,少數(shù)機(jī)器宕機(jī)个束,不會(huì)丟失數(shù)據(jù)慕购,不會(huì)導(dǎo)致不可用
消息可靠性
有較低的概率丟失數(shù)據(jù)
經(jīng)過參數(shù)優(yōu)化配置,可以做到0丟失
經(jīng)過參數(shù)優(yōu)化配置茬底,消息可以做到0丟失
功能支持
MQ領(lǐng)域的功能極其完備
基于erlang開發(fā)沪悲,所以并發(fā)能力很強(qiáng),性能極其好阱表,延時(shí)很低
MQ功能較為完善殿如,還是分布式的,擴(kuò)展性好
功能較為簡(jiǎn)單捶枢,主要支持簡(jiǎn)單的MQ功能握截,在大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算以及日志采集被大規(guī)模使用,是事實(shí)上的標(biāo)準(zhǔn)
優(yōu)劣勢(shì)總結(jié)
非常成熟烂叔,功能強(qiáng)大谨胞,在業(yè)內(nèi)大量的公司以及項(xiàng)目中都有應(yīng)用;
偶爾會(huì)有較低概率丟失消息蒜鸡;
而且現(xiàn)在社區(qū)以及國(guó)內(nèi)應(yīng)用都越來越少胯努,幾個(gè)月才發(fā)布一個(gè)版本牢裳;
而且確實(shí)主要是基于解耦和異步來用的,較少在大規(guī)模吞吐的場(chǎng)景中使用叶沛。
erlang語言開發(fā)蒲讯,性能極其好,延時(shí)很低灰署;
吞吐量到萬級(jí)判帮,MQ功能比較完備;
而且開源提供的管理界面非常棒溉箕,用起來很好用晦墙;
社區(qū)相對(duì)比較活躍,幾乎每個(gè)月都發(fā)布幾個(gè)版本分肴茄;
在國(guó)內(nèi)一些互聯(lián)網(wǎng)公司近幾年用rabbitmq也比較多一些晌畅;
但是問題也是顯而易見的,RabbitMQ確實(shí)吞吐量會(huì)低一些寡痰,這是因?yàn)樗龅膶?shí)現(xiàn)機(jī)制比較重抗楔;
而且erlang開發(fā),國(guó)內(nèi)有幾個(gè)公司有實(shí)力做erlang源碼級(jí)別的研究和定制拦坠?如果說你沒這個(gè)實(shí)力的話连躏,確實(shí)偶爾會(huì)有一些問題,你很難去看懂源碼贪婉,你公司對(duì)這個(gè)東西的掌控很弱反粥,基本職能依賴于開源社區(qū)的快速維護(hù)和修復(fù)bug;
而且rabbitmq集群動(dòng)態(tài)擴(kuò)展會(huì)很麻煩疲迂,不過這個(gè)我覺得還好才顿。其實(shí)主要是erlang語言本身帶來的問題。很難讀源碼尤蒿,很難定制和掌控郑气。
接口簡(jiǎn)單易用,而且畢竟在阿里大規(guī)模應(yīng)用過腰池,有阿里品牌保障尾组;
日處理消息上百億之多,可以做到大規(guī)模吞吐示弓,性能也非常好讳侨,分布式擴(kuò)展也很方便,社區(qū)維護(hù)還可以奏属,可靠性和可用性都是ok的跨跨,還可以支撐大規(guī)模的topic數(shù)量,支持復(fù)雜MQ業(yè)務(wù)場(chǎng)景;
而且一個(gè)很大的優(yōu)勢(shì)在于勇婴,阿里出品都是java系的忱嘹,我們可以自己閱讀源碼,定制自己公司的MQ耕渴,可以掌控拘悦;
社區(qū)活躍度相對(duì)較為一般,不過也還可以橱脸,文檔相對(duì)來說簡(jiǎn)單一些础米,然后接口這塊不是按照標(biāo)準(zhǔn)JMS規(guī)范走的有些系統(tǒng)要遷移需要修改大量代碼;
還有就是阿里出臺(tái)的技術(shù)慰技,你得做好這個(gè)技術(shù)萬一被拋棄椭盏,社區(qū)黃掉的風(fēng)險(xiǎn)组砚。
kafka的特點(diǎn)其實(shí)很明顯吻商,就是僅僅提供較少的核心功能,但是提供超高的吞吐量糟红,ms級(jí)的延遲艾帐,極高的可用性以及可靠性,而且分布式可以任意擴(kuò)展盆偿;
同時(shí)kafka最好是支撐較少的topic數(shù)量即可柒爸,保證其超高吞吐量;
而且kafka唯一的一點(diǎn)劣勢(shì)是有可能消息重復(fù)消費(fèi)事扭,那么對(duì)數(shù)據(jù)準(zhǔn)確性會(huì)造成極其輕微的影響捎稚,在大數(shù)據(jù)領(lǐng)域中以及日志采集中,這點(diǎn)輕微影響可以忽略求橄;
?這個(gè)特性天然適合大數(shù)據(jù)實(shí)時(shí)計(jì)算以及日志收集今野。
綜上所述,各種對(duì)比之后罐农,個(gè)人傾向的做法是:
一般的業(yè)務(wù)系統(tǒng)要引入MQ条霜,最早大家都用ActiveMQ,但是現(xiàn)在確實(shí)大家用的不多了涵亏,沒經(jīng)過大規(guī)模吞吐量場(chǎng)景的驗(yàn)證宰睡,社區(qū)也不是很活躍,所以大家還是算了吧气筋;
后來大家開始用RabbitMQ拆内,但是確實(shí)erlang語言阻止了大量的java工程師去深入研究和掌控他,對(duì)公司而言宠默,幾乎處于不可控的狀態(tài)麸恍,但是確實(shí)人是開源的,比較穩(wěn)定的支持光稼,活躍度也高或南;
不過現(xiàn)在確實(shí)越來越多的公司孩等,會(huì)去用RocketMQ,確實(shí)很不錯(cuò)采够,但是自己想好社區(qū)萬一突然黃掉的風(fēng)險(xiǎn)肄方,對(duì)自己公司技術(shù)實(shí)力有絕對(duì)自信的,推薦用RocketMQ蹬癌,否則回去老老實(shí)實(shí)用RabbitMQ吧权她,人家是活躍開源社區(qū),絕對(duì)不會(huì)黃逝薪;
所以中小型公司隅要,技術(shù)實(shí)力較為一般,技術(shù)挑戰(zhàn)不是特別高董济,用RabbitMQ是不錯(cuò)的選擇步清;大型公司,基礎(chǔ)架構(gòu)研發(fā)實(shí)力較強(qiáng)虏肾,用RocketMQ是很好的選擇廓啊;
如果是大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算、日志采集等場(chǎng)景封豪,用Kafka是業(yè)內(nèi)標(biāo)準(zhǔn)的谴轮,絕對(duì)沒問題,社區(qū)活躍度很高吹埠,絕對(duì)不會(huì)黃第步,何況幾乎是全世界這個(gè)領(lǐng)域的事實(shí)性規(guī)范。
五缘琅、消息隊(duì)列高可用的保證
1粘都、面試題
如何保證消息隊(duì)列的高可用啊胯杭?
2驯杜、面試官心理分析
如果有人問到你MQ的知識(shí),高可用是必問的做个,因?yàn)镸Q的缺點(diǎn)鸽心,剛才已經(jīng)說過了,有好多居暖,導(dǎo)致系統(tǒng)可用性降低顽频,等等。所以只要你用了MQ太闺,接下來問的一些要點(diǎn)肯定就是圍繞著MQ的那些缺點(diǎn)怎么來解決了糯景。
要是你傻乎乎的就干用了一個(gè)MQ,各種問題從來沒考慮過,那你就杯具了蟀淮,面試官對(duì)你的印象就是最住,只會(huì)簡(jiǎn)單實(shí)用一些技術(shù),沒任何思考怠惶,馬上對(duì)你的印象就不太好了涨缚。
3、面試題剖析
這個(gè)問題這么問是很好的策治,因?yàn)椴荒軉柲鉱afka的高可用性怎么保證芭骸?ActiveMQ的高可用性怎么保證巴ū埂茂翔?一個(gè)面試官要是這么問就顯得很沒水平,人家可能用的就是RabbitMQ履腋,沒用過Kafka珊燎,你上來問人家kafka干什么?這不是擺明了刁難人么府树。
所以有水平的面試官俐末,問的是MQ的高可用性怎么保證?這樣就是你用過哪個(gè)MQ奄侠,你就說說你對(duì)那個(gè)MQ的高可用性的理解。
1.RabbitMQ的高可用性
RabbitMQ是比較有代表性的载矿,因?yàn)槭腔谥鲝淖龈呖捎眯缘穆⒊保覀兙鸵运麨槔又v解第一種MQ的高可用性怎么實(shí)現(xiàn)。rabbitmq有三種模式:?jiǎn)螜C(jī)模式闷盔,普通集群模式弯洗,鏡像集群模式。
1)單機(jī)模式
就是demo級(jí)別的逢勾,一般就是你本地啟動(dòng)了玩玩兒的牡整,沒人生產(chǎn)用單機(jī)模式。
2)普通集群模式
意思就是在多臺(tái)機(jī)器上啟動(dòng)多個(gè)rabbitmq實(shí)例溺拱,每個(gè)機(jī)器啟動(dòng)一個(gè)逃贝。但是你創(chuàng)建的queue,只會(huì)放在一個(gè)rabbtimq實(shí)例上迫摔,但是每個(gè)實(shí)例都同步queue的元數(shù)據(jù)(queue的基本信息沐扳,沒用實(shí)際數(shù)據(jù))。你消費(fèi)的時(shí)候句占,實(shí)際上如果連接到了另外一個(gè)實(shí)例沪摄,那么那個(gè)實(shí)例會(huì)從queue所在實(shí)例上拉取數(shù)據(jù)過來。
這種方式確實(shí)很麻煩,也不怎么好杨拐,沒做到所謂的分布式祈餐,就是個(gè)普通集群。因?yàn)檫@導(dǎo)致你要么消費(fèi)者每次隨機(jī)連接一個(gè)實(shí)例然后拉取數(shù)據(jù)哄陶,要么固定連接那個(gè)queue所在實(shí)例消費(fèi)數(shù)據(jù)昼弟,前者有數(shù)據(jù)拉取的開銷,后者導(dǎo)致單實(shí)例性能瓶頸奕筐。
而且如果那個(gè)放queue的實(shí)例宕機(jī)了舱痘,會(huì)導(dǎo)致接下來其他實(shí)例就無法從那個(gè)實(shí)例拉取,如果你開啟了消息持久化离赫,讓rabbitmq落地存儲(chǔ)消息的話芭逝,消息不一定會(huì)丟,得等這個(gè)實(shí)例恢復(fù)了渊胸,然后才可以繼續(xù)從這個(gè)queue拉取數(shù)據(jù)旬盯。
所以這個(gè)事兒就比較尷尬了,這就沒有什么所謂的高可用性可言了翎猛,這種方案主要是提高吞吐量的胖翰,就是說讓集群中多個(gè)節(jié)點(diǎn)來服務(wù)某個(gè)queue的讀寫操作。
3)鏡像集群模式
這種模式切厘,才是所謂的rabbitmq的高可用模式萨咳,跟普通集群模式不一樣的是,你創(chuàng)建的queue疫稿,無論元數(shù)據(jù)還是queue里的消息都會(huì)存在于多個(gè)實(shí)例上培他,然后每次你寫消息到queue的時(shí)候,都會(huì)自動(dòng)把消息到多個(gè)實(shí)例的queue里進(jìn)行消息同步遗座。
這樣的話舀凛,好處在于,你任何一個(gè)機(jī)器宕機(jī)了途蒋,沒事兒猛遍,別的機(jī)器都可以用。壞處在于号坡,第一懊烤,這個(gè)性能開銷也太大了吧,消息同步所有機(jī)器筋帖,導(dǎo)致網(wǎng)絡(luò)帶寬壓力和消耗很重奸晴!第二,這么玩兒日麸,就沒有擴(kuò)展性可言了寄啼,如果某個(gè)queue負(fù)載很重逮光,你加機(jī)器,新增的機(jī)器也包含了這個(gè)queue的所有數(shù)據(jù)墩划,并沒有辦法線性擴(kuò)展你的queue涕刚。
那么怎么開啟這個(gè)鏡像集群模式呢?我這里簡(jiǎn)單說一下乙帮,避免面試人家問你你不知道杜漠,其實(shí)很簡(jiǎn)單rabbitmq有很好的管理控制臺(tái),就是在后臺(tái)新增一個(gè)策略察净,這個(gè)策略是鏡像集群模式的策略驾茴,指定的時(shí)候可以要求數(shù)據(jù)同步到所有節(jié)點(diǎn)的,也可以要求就同步到指定數(shù)量的節(jié)點(diǎn)氢卡,然后你再次創(chuàng)建queue的時(shí)候锈至,應(yīng)用這個(gè)策略,就會(huì)自動(dòng)將數(shù)據(jù)同步到其他的節(jié)點(diǎn)上去了译秦。
2.kafka的高可用性
kafka一個(gè)最基本的架構(gòu)認(rèn)識(shí):多個(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ì)列砚婆,就是說一個(gè)topic的數(shù)據(jù),是分散放在多個(gè)機(jī)器上的求摇,每個(gè)機(jī)器就放一部分?jǐn)?shù)據(jù)射沟。實(shí)際上rabbitmq之類的乔询,并不是分布式消息隊(duì)列正蛙,他就是傳統(tǒng)的消息隊(duì)列茫蛹,只不過提供了一些集群、HA的機(jī)制而已摔刁,因?yàn)闊o論怎么玩兒,rabbitmq一個(gè)queue的數(shù)據(jù)都是放在一個(gè)節(jié)點(diǎn)里的海蔽,鏡像集群下共屈,也是每個(gè)節(jié)點(diǎn)都放這個(gè)queue的完整數(shù)據(jù)。
kafka 0.8以前党窜,是沒有HA機(jī)制的拗引,就是任何一個(gè)broker宕機(jī)了,那個(gè)broker上的partition就廢了幌衣,沒法寫也沒法讀矾削,沒有什么高可用性可言壤玫。
kafka 0.8以后,提供了HA機(jī)制哼凯,就是replica副本機(jī)制欲间。每個(gè)partition的數(shù)據(jù)都會(huì)同步到吉他機(jī)器上,形成自己的多個(gè)replica副本断部。然后所有replica會(huì)選舉一個(gè)leader出來猎贴,那么生產(chǎn)和消費(fèi)都跟這個(gè)leader打交道,然后其他replica就是follower蝴光。寫的時(shí)候她渴,leader會(huì)負(fù)責(zé)把數(shù)據(jù)同步到所有follower上去,讀的時(shí)候就直接讀leader上數(shù)據(jù)即可蔑祟。只能讀寫leader趁耗?很簡(jiǎn)單,要是你可以隨意讀寫每個(gè)follower做瞪,那么就要care數(shù)據(jù)一致性的問題对粪,系統(tǒng)復(fù)雜度太高,很容易出問題装蓬。kafka會(huì)均勻的將一個(gè)partition的所有replica分布在不同的機(jī)器上著拭,這樣才可以提高容錯(cuò)性。
這么搞牍帚,就有所謂的高可用性了儡遮,因?yàn)槿绻硞€(gè)broker宕機(jī)了,沒事兒暗赶,那個(gè)broker上面的partition在其他機(jī)器上都有副本的鄙币,如果這上面有某個(gè)partition的leader,那么此時(shí)會(huì)重新選舉一個(gè)新的leader出來蹂随,大家繼續(xù)讀寫那個(gè)新的leader即可十嘿。這就有所謂的高可用性了。
寫數(shù)據(jù)的時(shí)候岳锁,生產(chǎn)者就寫leader绩衷,然后leader將數(shù)據(jù)落地寫本地磁盤,接著其他follower自己主動(dòng)從leader來pull數(shù)據(jù)激率。一旦所有follower同步好數(shù)據(jù)了咳燕,就會(huì)發(fā)送ack給leader,leader收到所有follower的ack之后乒躺,就會(huì)返回寫成功的消息給生產(chǎn)者招盲。(當(dāng)然,這只是其中一種模式嘉冒,還可以適當(dāng)調(diào)整這個(gè)行為)
消費(fèi)的時(shí)候曹货,只會(huì)從leader去讀咆繁,但是只有一個(gè)消息已經(jīng)被所有follower都同步成功返回ack的時(shí)候,這個(gè)消息才會(huì)被消費(fèi)者讀到控乾。
實(shí)際上這塊機(jī)制么介,講深了,是可以非常之深入的蜕衡,但是我還是回到我們這個(gè)課程的主題和定位壤短,聚焦面試,至少你聽到這里大致明白了kafka是如何保證高可用機(jī)制的了慨仿,對(duì)吧久脯?不至于一無所知,現(xiàn)場(chǎng)還能給面試官畫畫圖镰吆。要遇上面試官確實(shí)是kafka高手帘撰,深挖了問,那你只能說不好意思万皿,太深入的你沒研究過摧找。
六、消息隊(duì)列中重復(fù)消費(fèi)問題
1牢硅、面試題
如何保證消息不被重復(fù)消費(fèi)暗旁拧(如何保證消息消費(fèi)時(shí)的冪等性)?
2减余、面試官心里分析
其實(shí)這個(gè)很常見的一個(gè)問題综苔,這倆問題基本可以連起來問。既然是消費(fèi)消息位岔,那肯定要考慮考慮會(huì)不會(huì)重復(fù)消費(fèi)如筛?能不能避免重復(fù)消費(fèi)?或者重復(fù)消費(fèi)了也別造成系統(tǒng)異呈闾В可以嗎杨刨?這個(gè)是MQ領(lǐng)域的基本問題,其實(shí)本質(zhì)上還是問你使用消息隊(duì)列如何保證冪等性擦剑,這個(gè)是你架構(gòu)里要考慮的一個(gè)問題拭嫁。
3、面試題剖析
回答這個(gè)問題抓于,首先你別聽到重復(fù)消息這個(gè)事兒,就一無所知吧浇借,你先大概說一說可能會(huì)有哪些重復(fù)消費(fèi)的問題捉撮。
首先就是比如rabbitmq、rocketmq妇垢、kafka巾遭,都有可能會(huì)出現(xiàn)消費(fèi)重復(fù)消費(fèi)的問題肉康,正常。因?yàn)檫@問題通常不是mq自己保證的灼舍,是給你保證的吼和。然后我們挑一個(gè)kafka來舉個(gè)例子,說說怎么重復(fù)消費(fèi)吧骑素。
kafka實(shí)際上有個(gè)offset的概念炫乓,就是每個(gè)消息寫進(jìn)去,都有一個(gè)offset献丑,代表他的序號(hào)末捣,然后consumer消費(fèi)了數(shù)據(jù)之后,每隔一段時(shí)間创橄,會(huì)把自己消費(fèi)過的消息的offset提交一下箩做,代表我已經(jīng)消費(fèi)過了,下次我要是重啟啥的妥畏,你就讓我繼續(xù)從上次消費(fèi)到的offset來繼續(xù)消費(fèi)吧邦邦。
但是凡事總有意外,就是你有時(shí)候重啟系統(tǒng)醉蚁,看你怎么重啟了燃辖,如果碰到點(diǎn)著急的,直接kill進(jìn)程了馍管,再重啟郭赐。這會(huì)導(dǎo)致consumer有些消息處理了,但是沒來得及提交offset确沸,尷尬了捌锭。重啟之后,少數(shù)消息會(huì)再次消費(fèi)一次罗捎。
其實(shí)重復(fù)消費(fèi)不可怕观谦,可怕的是你沒考慮到重復(fù)消費(fèi)之后,怎么保證冪等性桨菜。
舉個(gè)例子吧豁状。假設(shè)你有個(gè)系統(tǒng),消費(fèi)一條往數(shù)據(jù)庫(kù)里插入一條倒得,要是你一個(gè)消息重復(fù)兩次泻红,你不就插入了兩條,這數(shù)據(jù)不就錯(cuò)了霞掺?但是你要是消費(fèi)到第二次的時(shí)候谊路,自己判斷一下已經(jīng)消費(fèi)過了,直接扔了菩彬,不就保留了一條數(shù)據(jù)缠劝?
一條數(shù)據(jù)重復(fù)出現(xiàn)兩次潮梯,數(shù)據(jù)庫(kù)里就只有一條數(shù)據(jù),這就保證了系統(tǒng)的冪等性惨恭。冪等性秉馏,通俗點(diǎn)說,就一個(gè)數(shù)據(jù)脱羡,或者一個(gè)請(qǐng)求萝究,給你重復(fù)來多次,你得確保對(duì)應(yīng)的數(shù)據(jù)是不會(huì)改變的轻黑,不能出錯(cuò)糊肤。
那所以第二個(gè)問題來了,怎么保證消息隊(duì)列消費(fèi)的冪等性氓鄙?其實(shí)還是得結(jié)合業(yè)務(wù)來思考馆揉,這里給幾個(gè)思路:
(1)比如你拿個(gè)數(shù)據(jù)要寫庫(kù),你先根據(jù)主鍵查一下抖拦,如果這數(shù)據(jù)都有了升酣,你就別插入了,update一下好吧态罪;
(2)比如你是寫redis噩茄,那沒問題了,反正每次都是set复颈,天然冪等性绩聘;
(3)比如你不是上面兩個(gè)場(chǎng)景,那做的稍微復(fù)雜一點(diǎn)耗啦,你需要讓生產(chǎn)者發(fā)送每條數(shù)據(jù)的時(shí)候凿菩,里面加一個(gè)全局唯一的id,類似訂單id之類的東西帜讲,然后你這里消費(fèi)到了之后衅谷,先根據(jù)這個(gè)id去比如redis里查一下,之前消費(fèi)過嗎似将?如果沒有消費(fèi)過获黔,你就處理,然后這個(gè)id寫redis在验。如果消費(fèi)過了玷氏,那你就別處理了,保證別重復(fù)處理相同的消息即可腋舌;
(4)還有比如基于數(shù)據(jù)庫(kù)的唯一鍵來保證重復(fù)數(shù)據(jù)不會(huì)重復(fù)插入多條预茄,重復(fù)數(shù)據(jù)拿到了以后我們插入的時(shí)候,因?yàn)橛形ㄒ绘I約束了,所以重復(fù)數(shù)據(jù)只會(huì)插入報(bào)錯(cuò)耻陕,不會(huì)導(dǎo)致數(shù)據(jù)庫(kù)中出現(xiàn)臟數(shù)據(jù)。
如何保證MQ的消費(fèi)是冪等性的刨沦,需要結(jié)合具體的業(yè)務(wù)來看诗宣。
七、消息隊(duì)列中數(shù)據(jù)丟失問題
1想诅、面試題
如何保證消息的可靠性傳輸(如何處理消息丟失的問題)召庞?
2、面試官心里分析
這個(gè)是肯定的来破,用mq有個(gè)基本原則篮灼,就是數(shù)據(jù)不能多一條,也不能少一條徘禁,不能多诅诱,就是剛才說的重復(fù)消費(fèi)和冪等性問題。不能少送朱,就是說這數(shù)據(jù)別搞丟了娘荡。那這個(gè)問題你必須得考慮一下。
如果說你這個(gè)是用mq來傳遞非常核心的消息驶沼,比如說計(jì)費(fèi)炮沐,扣費(fèi)的一些消息,因?yàn)槲乙郧把邪l(fā)過一個(gè)公司非常核心的計(jì)費(fèi)系統(tǒng)回怜,計(jì)費(fèi)系統(tǒng)是很重的一個(gè)業(yè)務(wù)大年,操作是很耗時(shí)的。所以說系統(tǒng)整體的架構(gòu)里面玉雾,實(shí)際上是將計(jì)費(fèi)做成異步化的翔试,然后中間就是加了一個(gè)MQ。
3抹凳、面試題剖析
這個(gè)丟數(shù)據(jù)遏餐,mq一般分為兩種,要么是mq自己弄丟了赢底,要么是我們消費(fèi)的時(shí)候弄丟了失都。咱們從rabbitmq和kafka分別來分析一下吧。
1.rabbitmq
1)生產(chǎn)者弄丟了數(shù)據(jù)
生產(chǎn)者將數(shù)據(jù)發(fā)送到rabbitmq的時(shí)候幸冻,可能數(shù)據(jù)就在半路給搞丟了粹庞,因?yàn)榫W(wǎng)絡(luò)啥的問題,都有可能洽损。
此時(shí)可以選擇用rabbitmq提供的事務(wù)功能庞溜,就是生產(chǎn)者發(fā)送數(shù)據(jù)之前開啟rabbitmq事務(wù)(channel.txSelect),然后發(fā)送消息,如果消息沒有成功被rabbitmq接收到流码,那么生產(chǎn)者會(huì)收到異常報(bào)錯(cuò)又官,此時(shí)就可以回滾事務(wù)(channel.txRollback),然后重試發(fā)送消息漫试;如果收到了消息六敬,那么可以提交事務(wù)(channel.txCommit)。但是問題是驾荣,rabbitmq事務(wù)機(jī)制一搞外构,基本上吞吐量會(huì)下來,因?yàn)樘男阅堋?/p>
所以一般來說播掷,如果你要確保說寫rabbitmq的消息別丟审编,可以開啟confirm模式,在生產(chǎn)者那里設(shè)置開啟confirm模式之后歧匈,你每次寫的消息都會(huì)分配一個(gè)唯一的id垒酬,然后如果寫入了rabbitmq中,rabbitmq會(huì)給你回傳一個(gè)ack消息眯亦,告訴你說這個(gè)消息ok了伤溉。如果rabbitmq沒能處理這個(gè)消息,會(huì)回調(diào)你一個(gè)nack接口妻率,告訴你這個(gè)消息接收失敗乱顾,你可以重試。而且你可以結(jié)合這個(gè)機(jī)制自己在內(nèi)存里維護(hù)每個(gè)消息id的狀態(tài)宫静,如果超過一定時(shí)間還沒接收到這個(gè)消息的回調(diào)走净,那么你可以重發(fā)。
事務(wù)機(jī)制和cnofirm機(jī)制最大的不同在于孤里,事務(wù)機(jī)制是同步的伏伯,你提交一個(gè)事務(wù)之后會(huì)阻塞在那兒,但是confirm機(jī)制是異步的捌袜,你發(fā)送個(gè)消息之后就可以發(fā)送下一個(gè)消息说搅,然后那個(gè)消息rabbitmq接收了之后會(huì)異步回調(diào)你一個(gè)接口通知你這個(gè)消息接收到了。
所以一般在生產(chǎn)者這塊避免數(shù)據(jù)丟失虏等,都是用confirm機(jī)制的弄唧。
2)rabbitmq弄丟了數(shù)據(jù)
就是rabbitmq自己弄丟了數(shù)據(jù),這個(gè)你必須開啟rabbitmq的持久化霍衫,就是消息寫入之后會(huì)持久化到磁盤候引,哪怕是rabbitmq自己掛了,恢復(fù)之后會(huì)自動(dòng)讀取之前存儲(chǔ)的數(shù)據(jù)敦跌,一般數(shù)據(jù)不會(huì)丟澄干。除非極其罕見的是,rabbitmq還沒持久化,自己就掛了麸俘,可能導(dǎo)致少量數(shù)據(jù)會(huì)丟失的辩稽,但是這個(gè)概率較小。
設(shè)置持久化有兩個(gè)步驟疾掰,第一個(gè)是創(chuàng)建queue的時(shí)候?qū)⑵湓O(shè)置為持久化的搂誉,這樣就可以保證rabbitmq持久化queue的元數(shù)據(jù),但是不會(huì)持久化queue里的數(shù)據(jù)静檬;第二個(gè)是發(fā)送消息的時(shí)候?qū)⑾⒌膁eliveryMode設(shè)置為2,就是將消息設(shè)置為持久化的并级,此時(shí)rabbitmq就會(huì)將消息持久化到磁盤上去拂檩。必須要同時(shí)設(shè)置這兩個(gè)持久化才行,rabbitmq哪怕是掛了嘲碧,再次重啟稻励,也會(huì)從磁盤上重啟恢復(fù)queue,恢復(fù)這個(gè)queue里的數(shù)據(jù)愈涩。
而且持久化可以跟生產(chǎn)者那邊的confirm機(jī)制配合起來望抽,只有消息被持久化到磁盤之后,才會(huì)通知生產(chǎn)者ack了履婉,所以哪怕是在持久化到磁盤之前煤篙,rabbitmq掛了,數(shù)據(jù)丟了毁腿,生產(chǎn)者收不到ack辑奈,你也是可以自己重發(fā)的。
哪怕是你給rabbitmq開啟了持久化機(jī)制已烤,也有一種可能鸠窗,就是這個(gè)消息寫到了rabbitmq中,但是還沒來得及持久化到磁盤上胯究,結(jié)果不巧稍计,此時(shí)rabbitmq掛了,就會(huì)導(dǎo)致內(nèi)存里的一點(diǎn)點(diǎn)數(shù)據(jù)會(huì)丟失裕循。
3)消費(fèi)端弄丟了數(shù)據(jù)
rabbitmq如果丟失了數(shù)據(jù)臣嚣,主要是因?yàn)槟阆M(fèi)的時(shí)候,剛消費(fèi)到费韭,還沒處理茧球,結(jié)果進(jìn)程掛了,比如重啟了星持,那么就尷尬了抢埋,rabbitmq認(rèn)為你都消費(fèi)了,這數(shù)據(jù)就丟了。
這個(gè)時(shí)候得用rabbitmq提供的ack機(jī)制揪垄,簡(jiǎn)單來說穷吮,就是你關(guān)閉rabbitmq自動(dòng)ack,可以通過一個(gè)api來調(diào)用就行饥努,然后每次你自己代碼里確保處理完的時(shí)候捡鱼,再程序里ack一把。這樣的話酷愧,如果你還沒處理完驾诈,不就沒有ack?那rabbitmq就認(rèn)為你還沒處理完溶浴,這個(gè)時(shí)候rabbitmq會(huì)把這個(gè)消費(fèi)分配給別的consumer去處理乍迄,消息是不會(huì)丟的。
2.kafka
1)消費(fèi)端弄丟了數(shù)據(jù)
唯一可能導(dǎo)致消費(fèi)者弄丟數(shù)據(jù)的情況士败,就是說闯两,你那個(gè)消費(fèi)到了這個(gè)消息,然后消費(fèi)者那邊自動(dòng)提交了offset谅将,讓kafka以為你已經(jīng)消費(fèi)好了這個(gè)消息漾狼,其實(shí)你剛準(zhǔn)備處理這個(gè)消息,你還沒處理饥臂,你自己就掛了逊躁,此時(shí)這條消息就丟咯。
這不是一樣么擅笔,大家都知道kafka會(huì)自動(dòng)提交offset志衣,那么只要關(guān)閉自動(dòng)提交offset,在處理完之后自己手動(dòng)提交offset猛们,就可以保證數(shù)據(jù)不會(huì)丟念脯。但是此時(shí)確實(shí)還是會(huì)重復(fù)消費(fèi),比如你剛處理完弯淘,還沒提交offset绿店,結(jié)果自己掛了,此時(shí)肯定會(huì)重復(fù)消費(fèi)一次庐橙,自己保證冪等性就好了假勿。
生產(chǎn)環(huán)境碰到的一個(gè)問題,就是說我們的kafka消費(fèi)者消費(fèi)到了數(shù)據(jù)之后是寫到一個(gè)內(nèi)存的queue里先緩沖一下态鳖,結(jié)果有的時(shí)候转培,你剛把消息寫入內(nèi)存queue,然后消費(fèi)者會(huì)自動(dòng)提交offset浆竭。
然后此時(shí)我們重啟了系統(tǒng)浸须,就會(huì)導(dǎo)致內(nèi)存queue里還沒來得及處理的數(shù)據(jù)就丟失了惨寿。
2)kafka弄丟了數(shù)據(jù)
這塊比較常見的一個(gè)場(chǎng)景,就是kafka某個(gè)broker宕機(jī)删窒,然后重新選舉partiton的leader時(shí)裂垦。大家想想,要是此時(shí)其他的follower剛好還有些數(shù)據(jù)沒有同步肌索,結(jié)果此時(shí)leader掛了蕉拢,然后選舉某個(gè)follower成leader之后,他不就少了一些數(shù)據(jù)诚亚?這就丟了一些數(shù)據(jù)啊晕换。
所以此時(shí)一般是要求起碼設(shè)置如下4個(gè)參數(shù):
給這個(gè)topic設(shè)置replication.factor參數(shù):這個(gè)值必須大于1,要求每個(gè)partition必須有至少2個(gè)副本站宗;
在kafka服務(wù)端設(shè)置min.insync.replicas參數(shù):這個(gè)值必須大于1届巩,這個(gè)是要求一個(gè)leader至少感知到有至少一個(gè)follower還跟自己保持聯(lián)系,沒掉隊(duì)份乒,這樣才能確保leader掛了還有一個(gè)follower吧;
在producer端設(shè)置acks=all:這個(gè)是要求每條數(shù)據(jù)腕唧,必須是寫入所有replica之后或辖,才能認(rèn)為是寫成功了;
在producer端設(shè)置retries=MAX(很大很大很大的一個(gè)值枣接,無限次重試的意思):這個(gè)是要求一旦寫入失敗颂暇,就無限重試,卡在這里了但惶;
我們生產(chǎn)環(huán)境就是按照上述要求配置的耳鸯,這樣配置之后,至少在kafka broker端就可以保證在leader所在broker發(fā)生故障膀曾,進(jìn)行l(wèi)eader切換時(shí)县爬,數(shù)據(jù)不會(huì)丟失。
3)生產(chǎn)者會(huì)不會(huì)弄丟數(shù)據(jù)
如果按照上述的思路設(shè)置了ack=all添谊,一定不會(huì)丟财喳,要求是,你的leader接收到消息斩狱,所有的follower都同步到了消息之后耳高,才認(rèn)為本次寫成功了。如果沒滿足這個(gè)條件所踊,生產(chǎn)者會(huì)自動(dòng)不斷的重試泌枪,重試無限次。
八秕岛、消息隊(duì)列順序性的保證
1碌燕、面試題
如何保證消息的順序性误证?
2、面試官心里分析
其實(shí)這個(gè)也是用MQ的時(shí)候必問的話題陆蟆,第一看看你了解不了解順序這個(gè)事兒雷厂?第二看看你有沒有辦法保證消息是有順序的?這個(gè)生產(chǎn)系統(tǒng)中常見的問題叠殷。
3改鲫、面試題剖析
舉個(gè)例子,比如我們要做一個(gè)mysql binlog同步的系統(tǒng)林束,日同步數(shù)據(jù)要達(dá)到上億像棘。常見的一點(diǎn)在于說大數(shù)據(jù)team,就需要同步一個(gè)mysql庫(kù)過來壶冒,對(duì)公司的業(yè)務(wù)系統(tǒng)的數(shù)據(jù)做各種復(fù)雜的操作缕题。
你在mysql里增刪改一條數(shù)據(jù),對(duì)應(yīng)出來了增刪改3條binlog胖腾,接著這三條binlog發(fā)送到MQ里面烟零,到消費(fèi)出來依次執(zhí)行,起碼得保證人家是按照順序來的吧咸作?不然本來是:增加锨阿、修改、刪除记罚;你楞是換了順序給執(zhí)行成刪除墅诡、修改、增加桐智,不全錯(cuò)了么末早。
本來這個(gè)數(shù)據(jù)同步過來,應(yīng)該最后這個(gè)數(shù)據(jù)被刪除了说庭;結(jié)果你搞錯(cuò)了這個(gè)順序然磷,最后這個(gè)數(shù)據(jù)保留下來了,數(shù)據(jù)同步就出錯(cuò)了口渔。
先看看順序會(huì)錯(cuò)亂的倆場(chǎng)景:
rabbitmq:一個(gè)queue样屠,多個(gè)consumer,這不明顯亂了
kafka:一個(gè)topic缺脉,一個(gè)partition痪欲,一個(gè)consumer,內(nèi)部多線程攻礼,這不也明顯亂了
那如何保證消息的順序性呢业踢?其實(shí)也簡(jiǎn)單。在rabbitmq中可以拆分多個(gè)queue礁扮,每個(gè)queue一個(gè)consumer知举,就是多一些queue而已瞬沦,確實(shí)是麻煩點(diǎn);或者就一個(gè)queue但是對(duì)應(yīng)一個(gè)consumer雇锡,然后這個(gè)consumer內(nèi)部用內(nèi)存隊(duì)列做排隊(duì)逛钻,然后分發(fā)給底層不同的worker來處理。
而對(duì)于kafka來說锰提,一個(gè)topic曙痘,一個(gè)partition,一個(gè)consumer立肘,內(nèi)部單線程消費(fèi)边坤,寫N個(gè)內(nèi)存queue,然后N個(gè)線程分別消費(fèi)一個(gè)內(nèi)存queue即可谅年。
九茧痒、消息積壓?jiǎn)栴}
1、面試題
如何解決消息隊(duì)列的延時(shí)以及過期失效問題融蹂?消息隊(duì)列滿了以后該怎么處理旺订?有幾百萬消息持續(xù)積壓幾小時(shí),說說怎么解決超燃?
2耸峭、面試官心里分析
你看這問法,其實(shí)本質(zhì)針對(duì)的場(chǎng)景淋纲,都是說延柠,可能你的消費(fèi)端出了問題纫溃,不消費(fèi)了魔市,或者消費(fèi)的極其極其慢遵绰。接著就坑爹了鞠眉,可能你的消息隊(duì)列集群的磁盤都快寫滿了征冷,都沒人消費(fèi)澄耍,這個(gè)時(shí)候怎么辦浊伙?或者是整個(gè)這就積壓了幾個(gè)小時(shí)样漆,你這個(gè)時(shí)候怎么辦为障?或者是你積壓的時(shí)間太長(zhǎng)了,導(dǎo)致比如rabbitmq設(shè)置了消息過期時(shí)間后就沒了怎么辦放祟?
所以就這事兒鳍怨,其實(shí)線上挺常見的,一般不出跪妥,一出就是大case鞋喇,一般常見于,舉個(gè)例子眉撵,消費(fèi)端每次消費(fèi)之后要寫mysql侦香,結(jié)果mysql掛了落塑,消費(fèi)端hang那兒了,不動(dòng)了罐韩『读蓿或者是消費(fèi)端出了個(gè)什么岔子,導(dǎo)致消費(fèi)速度極其慢散吵。
3龙考、面試題分析
關(guān)于這個(gè)事兒,我們一個(gè)一個(gè)來梳理吧错蝴,先假設(shè)一個(gè)場(chǎng)景洲愤,我們現(xiàn)在消費(fèi)端出故障了,然后大量消息在mq里積壓顷锰,現(xiàn)在事故了柬赐,慌了。
(1)大量消息在mq里積壓了幾個(gè)小時(shí)了還沒解決
幾千萬條數(shù)據(jù)在MQ里積壓了七八個(gè)小時(shí)官紫,從下午4點(diǎn)多肛宋,積壓到了晚上很晚,10點(diǎn)多束世,11點(diǎn)多酝陈。這個(gè)時(shí)候要不然就是修復(fù)consumer的問題,讓他恢復(fù)消費(fèi)速度毁涉,然后傻傻的等待幾個(gè)小時(shí)消費(fèi)完畢沉帮。這個(gè)肯定不能在面試的時(shí)候說吧。
一個(gè)消費(fèi)者一秒是1000條贫堰,一秒3個(gè)消費(fèi)者是3000條穆壕,一分鐘是18萬條,那一個(gè)小時(shí)就是1000多萬條其屏。所以如果你積壓了幾百萬到上千萬的數(shù)據(jù)喇勋,即使消費(fèi)者恢復(fù)了,也需要大概1小時(shí)的時(shí)間才能恢復(fù)過來偎行。
一般這個(gè)時(shí)候川背,只能操作臨時(shí)緊急擴(kuò)容了,具體操作步驟和思路如下:
先修復(fù)consumer的問題蛤袒,確保其恢復(fù)消費(fèi)速度熄云,然后將現(xiàn)有cnosumer都停掉
新建一個(gè)topic,partition是原來的10倍妙真,臨時(shí)建立好原先10倍或者20倍的queue數(shù)量
然后寫一個(gè)臨時(shí)的分發(fā)數(shù)據(jù)的consumer程序皱碘,這個(gè)程序部署上去消費(fèi)積壓的數(shù)據(jù),消費(fèi)之后不做耗時(shí)的處理隐孽,直接均勻輪詢寫入臨時(shí)建立好的10倍數(shù)量的queue
接著臨時(shí)征用10倍的機(jī)器來部署consumer癌椿,每一批consumer消費(fèi)一個(gè)臨時(shí)queue的數(shù)據(jù)
這種做法相當(dāng)于是臨時(shí)將queue資源和consumer資源擴(kuò)大10倍健蕊,以正常的10倍速度來消費(fèi)數(shù)據(jù)
等快速消費(fèi)完積壓數(shù)據(jù)之后,得恢復(fù)原先部署架構(gòu)踢俄,重新用原先的consumer機(jī)器來消費(fèi)消息
(2)這里我們假設(shè)再來第二個(gè)坑
假設(shè)你用的是rabbitmq缩功,rabbitmq是可以設(shè)置過期時(shí)間的,就是TTL都办,如果消息在queue中積壓超過一定的時(shí)間就會(huì)被rabbitmq給清理掉嫡锌,這個(gè)數(shù)據(jù)就沒了。那這就是第二個(gè)坑了琳钉。這就不是說數(shù)據(jù)會(huì)大量積壓在mq里势木,而是大量的數(shù)據(jù)會(huì)直接搞丟。
這個(gè)情況下歌懒,就不是說要增加consumer消費(fèi)積壓的消息啦桌,因?yàn)閷?shí)際上沒啥積壓,而是丟了大量的消息及皂。我們可以采取一個(gè)方案甫男,就是批量重導(dǎo)。就是大量積壓的時(shí)候验烧,我們當(dāng)時(shí)就直接丟棄數(shù)據(jù)了板驳,然后等過了高峰期以后,比如大家一起喝咖啡熬夜到晚上12點(diǎn)以后碍拆,用戶都睡覺了若治。
這個(gè)時(shí)候我們就開始寫程序,將丟失的那批數(shù)據(jù)感混,寫個(gè)臨時(shí)程序直砂,一點(diǎn)一點(diǎn)的查出來,然后重新灌入mq里面去浩习,把白天丟的數(shù)據(jù)給他補(bǔ)回來。也只能是這樣了济丘。
假設(shè)1萬個(gè)訂單積壓在mq里面谱秽,沒有處理,其中1000個(gè)訂單都丟了摹迷,你只能手動(dòng)寫程序把那1000個(gè)訂單給查出來疟赊,手動(dòng)發(fā)到mq里去再補(bǔ)一次
(3)然后我們?cè)賮砑僭O(shè)第三個(gè)坑
如果走的方式是消息積壓在mq里,那么如果你很長(zhǎng)時(shí)間都沒處理掉峡碉,此時(shí)導(dǎo)致mq都快寫滿了近哟,咋辦?這個(gè)還有別的辦法嗎鲫寄?沒有吉执,誰讓你第一個(gè)方案執(zhí)行的太慢了疯淫,你臨時(shí)寫程序,接入數(shù)據(jù)來消費(fèi)戳玫,消費(fèi)一個(gè)丟棄一個(gè)熙掺,都不要了,快速消費(fèi)掉所有的消息咕宿。然后走第二個(gè)方案币绩,到了晚上再補(bǔ)數(shù)據(jù)吧。
十府阀、怎么自己設(shè)計(jì)一個(gè)消息隊(duì)列中間件
1缆镣、面試題
如果讓你寫一個(gè)消息隊(duì)列,該如何進(jìn)行架構(gòu)設(shè)計(jì)笆哉恪董瞻?說一下你的思路。
2川队、面試官心里分析
其實(shí)聊到這個(gè)問題力细,一般面試官要考察兩塊:
(1)你有沒有對(duì)某一個(gè)消息隊(duì)列做過較為深入的原理的了解,或者從整體了解把握住一個(gè)mq的架構(gòu)原理
(2)看看你的設(shè)計(jì)能力固额,給你一個(gè)常見的系統(tǒng)眠蚂,就是消息隊(duì)列系統(tǒng),看看你能不能從全局把握一下整體架構(gòu)設(shè)計(jì)斗躏,給出一些關(guān)鍵點(diǎn)出來
說實(shí)話逝慧,一般面類似問題的時(shí)候,大部分人基本都會(huì)蒙啄糙,因?yàn)槠綍r(shí)從來沒有思考過類似的問題笛臣,大多數(shù)人就是平時(shí)埋頭用,從來不去思考背后的一些東西隧饼。類似的問題還有沈堡,如果讓你來設(shè)計(jì)一個(gè)spring框架你會(huì)怎么做?如果讓你來設(shè)計(jì)一個(gè)dubbo框架你會(huì)怎么做燕雁?如果讓你來設(shè)計(jì)一個(gè)mybatis框架你會(huì)怎么做诞丽?
3、面試題剖析
其實(shí)回答這類問題拐格,說白了僧免,不求你看過那技術(shù)的源碼,起碼你大概知道那個(gè)技術(shù)的基本原理捏浊,核心組成部分懂衩,基本架構(gòu)構(gòu)成,然后參照一些開源的技術(shù)把一個(gè)系統(tǒng)設(shè)計(jì)出來的思路說一下就好。
比如說這個(gè)消息隊(duì)列系統(tǒng)浊洞,我們來從以下幾個(gè)角度來考慮一下:
(1)首先這個(gè)mq得支持可伸縮性吧牵敷,就是需要的時(shí)候快速擴(kuò)容,就可以增加吞吐量和容量沛申,那怎么搞劣领?設(shè)計(jì)個(gè)分布式的系統(tǒng)唄,參照一下kafka的設(shè)計(jì)理念铁材,broker -> topic -> partition尖淘,每個(gè)partition放一個(gè)機(jī)器,就存一部分?jǐn)?shù)據(jù)著觉。如果現(xiàn)在資源不夠了村生,簡(jiǎn)單啊,給topic增加partition饼丘,然后做數(shù)據(jù)遷移趁桃,增加機(jī)器,不就可以存放更多數(shù)據(jù)肄鸽,提供更高的吞吐量了卫病?
(2)其次你得考慮一下這個(gè)mq的數(shù)據(jù)要不要落地磁盤吧?那肯定要了典徘,落磁盤蟀苛,才能保證別進(jìn)程掛了數(shù)據(jù)就丟了。那落磁盤的時(shí)候怎么落按濉帜平?順序?qū)懀@樣就沒有磁盤隨機(jī)讀寫的尋址開銷梅鹦,磁盤順序讀寫的性能是很高的裆甩,這就是kafka的思路。
(3)其次你考慮一下你的mq的可用性捌胨簟嗤栓?這個(gè)事兒,具體參考我們之前可用性那個(gè)環(huán)節(jié)講解的kafka的高可用保障機(jī)制箍邮。多副本 -> leader & follower -> broker掛了重新選舉leader即可對(duì)外服務(wù)茉帅。
(4)能不能支持?jǐn)?shù)據(jù)0丟失啊媒殉?可以的,參考我們之前說的那個(gè)kafka數(shù)據(jù)零丟失方案
其實(shí)一個(gè)mq肯定是很復(fù)雜的摔敛,面試官問你這個(gè)問題廷蓉,其實(shí)是個(gè)開放題,他就是看看你有沒有從架構(gòu)角度整體構(gòu)思和設(shè)計(jì)的思維以及能力。確實(shí)這個(gè)問題可以刷掉一大批人桃犬,因?yàn)榇蟛糠秩似綍r(shí)不思考這些東西刹悴。