解耦
看這么個(gè)場景。A 系統(tǒng)發(fā)送數(shù)據(jù)到 BCD 三個(gè)系統(tǒng),通過接口調(diào)用發(fā)送。如果 E 系統(tǒng)也要這個(gè)數(shù)據(jù)呢?那如果 C 系統(tǒng)現(xiàn)在不需要了呢?A 系統(tǒng)負(fù)責(zé)人幾乎崩潰......

在這個(gè)場景中,A 系統(tǒng)跟其它各種亂七八糟的系統(tǒng)嚴(yán)重耦合,A 系統(tǒng)產(chǎn)生一條比較關(guān)鍵的數(shù)據(jù),很多系統(tǒng)都需要 A 系統(tǒng)將這個(gè)數(shù)據(jù)發(fā)送過來。A 系統(tǒng)要時(shí)時(shí)刻刻考慮 BCDE 四個(gè)系統(tǒng)如果掛了該咋辦?要不要重發(fā),要不要把消息存起來?頭發(fā)都白了??!
如果使用 MQ,A 系統(tǒng)產(chǎn)生一條數(shù)據(jù),發(fā)送到 MQ 里面去,哪個(gè)系統(tǒng)需要數(shù)據(jù)自己去 MQ 里面消費(fèi)。如果新系統(tǒng)需要數(shù)據(jù),直接從 MQ 里消費(fèi)即可;如果某個(gè)系統(tǒng)不需要這條數(shù)據(jù)了,就取消對 MQ 消息的消費(fèi)即可。這樣下來,A 系統(tǒng)壓根兒不需要去考慮要給誰發(fā)送數(shù)據(jù),不需要維護(hù)這個(gè)代碼,也不需要考慮人家是否調(diào)用成功、失敗超時(shí)等情況。

總結(jié):通過一個(gè) MQ,Pub/Sub 發(fā)布訂閱消息這么一個(gè)模型,A 系統(tǒng)就跟其它系統(tǒng)徹底解耦了。
面試技巧:你需要去考慮一下你負(fù)責(zé)的系統(tǒ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)的解耦。在簡歷中體現(xiàn)出來這塊東西,用 MQ 作解耦。

再來看一個(gè)場景,A 系統(tǒng)接收一個(gè)請求,需要在自己本地寫庫,還需要在 BCD 三個(gè)系統(tǒng)寫庫,自己本地寫庫要 3ms,BCD 三個(gè)系統(tǒng)分別寫庫要 300ms、450ms、200ms。最終請求總延時(shí)是 3 + 300 + 450 + 200 = 953ms,接近 1s,用戶感覺搞個(gè)什么東西,慢死了慢死了。用戶通過瀏覽器發(fā)起請求,等待個(gè) 1s,這幾乎是不可接受的。

一般互聯(lián)網(wǎng)類的企業(yè),對于用戶直接的操作,一般要求是每個(gè)請求都必須在 200 ms 以內(nèi)完成,對用戶幾乎是無感知的。
如果使用 MQ,那么 A 系統(tǒng)連續(xù)發(fā)送 3 條消息到 MQ 隊(duì)列中,假如耗時(shí) 5ms,A 系統(tǒng)從接受一個(gè)請求到返回響應(yīng)給用戶,總時(shí)長是 3 + 5 = 8ms,對于用戶而言,其實(shí)感覺上就是點(diǎn)個(gè)按鈕,8ms 以后就直接返回了,爽!網(wǎng)站做得真好,真快!

削峰
每天 0:00 到 12:00,A 系統(tǒng)風(fēng)平浪靜,每秒并發(fā)請求數(shù)量就 50 個(gè)。結(jié)果每次一到 12:00 ~ 13:00 ,每秒并發(fā)請求數(shù)量突然會(huì)暴增到 5k+ 條。但是系統(tǒng)是直接基于 MySQL的,大量的請求涌入 MySQL,每秒鐘對 MySQL 執(zhí)行約 5k 條 SQL。
一般的 MySQL,扛到每秒 2k 個(gè)請求就差不多了,如果每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,導(dǎo)致系統(tǒng)崩潰,用戶也就沒法再使用系統(tǒng)了。
但是高峰期一過,到了下午的時(shí)候,就成了低峰期,可能也就 1w 的用戶同時(shí)在網(wǎng)站上操作,每秒中的請求數(shù)量可能也就 50 個(gè)請求,對整個(gè)系統(tǒng)幾乎沒有任何的壓力。
[圖片上傳失敗...(image-30f012-1543979244454)]
如果使用 MQ,每秒 5k 個(gè)請求寫入 MQ,A 系統(tǒng)每秒鐘最多處理 2k 個(gè)請求,因?yàn)?MySQL 每秒鐘最多處理 2k 個(gè)。A 系統(tǒng)從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個(gè)請求,不要超過自己每秒能處理的最大請求數(shù)量就 ok,這樣下來,哪怕是高峰期的時(shí)候,A 系統(tǒng)也絕對不會(huì)掛掉。而 MQ 每秒鐘 5k 個(gè)請求進(jìn)來,就 2k 個(gè)請求出去,結(jié)果就導(dǎo)致在中午高峰期(1 個(gè)小時(shí)),可能有幾十萬甚至幾百萬的請求積壓在 MQ 中。
[圖片上傳失敗...(image-196bfe-1543979244454)]
這個(gè)短暫的高峰期積壓是 ok 的,因?yàn)楦叻迤谶^了之后,每秒鐘就 50 個(gè)請求進(jìn) MQ,但是 A 系統(tǒng)依然會(huì)按照每秒 2k 個(gè)請求的速度在處理。所以說,只要高峰期一過,A 系統(tǒng)就會(huì)快速將積壓的消息給解決掉。
消息隊(duì)列有什么優(yōu)缺點(diǎn)
優(yōu)點(diǎn)上面已經(jīng)說了,就是在特殊場景下有其對應(yīng)的好處,解耦、異步、削峰。
缺點(diǎn)有以下幾個(gè):
系統(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)崩潰的,你不就完了?如何保證消息隊(duì)列的高可用,可以點(diǎn)擊這里查看。系統(tǒng)復(fù)雜度提高
硬生生加個(gè) MQ 進(jìn)來,你怎么保證消息沒有重復(fù)消費(fèi)?怎么處理消息丟失的情況?怎么保證消息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。一致性問題
A 系統(tǒng)處理完了直接返回成功了,人都以為你這個(gè)請求就成功了;但是問題是,要是 BCD 三個(gè)系統(tǒng)那里,BD 兩個(gè)系統(tǒng)寫庫成功了,結(jié)果 C 系統(tǒng)寫庫失敗了,咋整?你這數(shù)據(jù)就不一致了。
所以消息隊(duì)列實(shí)際是一種非常復(fù)雜的架構(gòu),你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術(shù)方案和架構(gòu)來規(guī)避掉,做好之后,你會(huì)發(fā)現(xiàn),媽呀,系統(tǒng)復(fù)雜度提升了一個(gè)數(shù)量級,也許是復(fù)雜了 10 倍。但是關(guān)鍵時(shí)刻,用,還是得用的。
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么優(yōu)缺點(diǎn)?
| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 單機(jī)吞吐量 | 萬級,比 RocketMQ、Kafka 低一個(gè)數(shù)量級 | 同 ActiveMQ | 10 萬級,支撐高吞吐 | 10 萬級,高吞吐,一般配合大數(shù)據(jù)類的系統(tǒng)來進(jìn)行實(shí)時(shí)數(shù)據(jù)計(jì)算、日志采集等場景 |
| topic 數(shù)量對吞吐量的影響 | topic 可以達(dá)到幾百/幾千的級別,吞吐量會(huì)有較小幅度的下降,這是 RocketMQ 的一大優(yōu)勢,在同等機(jī)器下,可以支撐大量的 topic | topic 從幾十到幾百個(gè)時(shí)候,吞吐量會(huì)大幅度下降,在同等機(jī)器下,Kafka 盡量保證 topic 數(shù)量不要過多,如果要支撐大規(guī)模的 topic,需要增加更多的機(jī)器資源 | ||
| 時(shí)效性 | ms 級 | 微秒級,這是 RabbitMQ 的一大特點(diǎn),延遲最低 | ms 級 | 延遲在 ms 級以內(nèi) |
| 可用性 | 高,基于主從架構(gòu)實(shí)現(xiàn)高可用 | 同 ActiveMQ | 非常高,分布式架構(gòu) | 非常高,分布式,一個(gè)數(shù)據(jù)多個(gè)副本,少數(shù)機(jī)器宕機(jī),不會(huì)丟失數(shù)據(jù),不會(huì)導(dǎo)致不可用 |
| 消息可靠性 | 有較低的概率丟失數(shù)據(jù) | 經(jīng)過參數(shù)優(yōu)化配置,可以做到 0 丟失 | 同 RocketMQ | |
| 功能支持 | MQ 領(lǐng)域的功能極其完備 | 基于 erlang 開發(fā),并發(fā)能力很強(qiáng),性能極好,延時(shí)很低 | MQ 功能較為完善,還是分布式的,擴(kuò)展性好 | 功能較為簡單,主要支持簡單的 MQ 功能,在大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算以及日志采集被大規(guī)模使用 |
綜上,各種對比之后,有如下建議:
一般的業(yè)務(wù)系統(tǒng)要引入 MQ,最早大家都用 ActiveMQ,但是現(xiàn)在確實(shí)大家用的不多了,沒經(jīng)過大規(guī)模吞吐量場景的驗(yàn)證,社區(qū)也不是很活躍,所以大家還是算了吧,我個(gè)人不推薦用這個(gè)了;
后來大家開始用 RabbitMQ,但是確實(shí) erlang 語言阻止了大量的 Java 工程師去深入研究和掌控它,對公司而言,幾乎處于不可控的狀態(tài),但是確實(shí)人家是開源的,比較穩(wěn)定的支持,活躍度也高;
不過現(xiàn)在確實(shí)越來越多的公司,會(huì)去用 RocketMQ,確實(shí)很不錯(cuò)(阿里出品),但社區(qū)可能有突然黃掉的風(fēng)險(xiǎn),對自己公司技術(shù)實(shí)力有絕對自信的,推薦用 RocketMQ,否則回去老老實(shí)實(shí)用 RabbitMQ 吧,人家有活躍的開源社區(qū),絕對不會(huì)黃。
所以中小型公司,技術(shù)實(shí)力較為一般,技術(shù)挑戰(zhàn)不是特別高,用 RabbitMQ 是不錯(cuò)的選擇;大型公司,基礎(chǔ)架構(gòu)研發(fā)實(shí)力較強(qiáng),用 RocketMQ 是很好的選擇。
如果是大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算、日志采集等場景,用 Kafka 是業(yè)內(nèi)標(biāo)準(zhǔn)的,絕對沒問題,社區(qū)活躍度很高,絕對不會(huì)黃,何況幾乎是全世界這個(gè)領(lǐng)域的事實(shí)性規(guī)范。