自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

90%的Java程序員,都扛不住這波消息中間件的面試四連炮!

開發(fā) 前端
大家平時(shí)也有用到一些消息中間件(MQ),但是對(duì)其理解可能僅停留在會(huì)使用API能實(shí)現(xiàn)生產(chǎn)消息、消費(fèi)消息就完事了。

本文經(jīng)授權(quán)轉(zhuǎn)自公眾號(hào):石杉的架構(gòu)筆記

概述

大家平時(shí)也有用到一些消息中間件(MQ),但是對(duì)其理解可能僅停留在會(huì)使用API能實(shí)現(xiàn)生產(chǎn)消息、消費(fèi)消息就完事了。

對(duì)MQ更加深入的問(wèn)題,可能很多人沒(méi)怎么思考過(guò)。

比如,你跳槽面試時(shí),如果面試官看到你簡(jiǎn)歷上寫了,熟練掌握消息中間件,那么很可能給你發(fā)起如下 4 個(gè)面試連環(huán)炮!

[[269714]]

  • 為什么要使用MQ?
  • 使用了MQ之后有什么優(yōu)缺點(diǎn)?
  • 怎么保證MQ消息不丟失?
  • 怎么保證MQ的高可用性?

本文將通過(guò)一些場(chǎng)景,配合著通俗易懂的語(yǔ)言和多張手繪彩圖,討論一下這些問(wèn)題。

為什么要使用MQ?

相信大家也聽(tīng)過(guò)這樣的一句話:好的架構(gòu)不是設(shè)計(jì)出來(lái)的,是演進(jìn)出來(lái)的。

這句話在引入MQ的場(chǎng)景同樣適用,使用MQ必定有其道理,是用來(lái)解決實(shí)際問(wèn)題的。而不是看見(jiàn)別人用了,我也用著玩兒一下。

其實(shí)使用MQ的場(chǎng)景有挺多的,但是比較核心的有3個(gè):

異步、解耦、削峰填谷

異步

我們通過(guò)實(shí)際案例說(shuō)明:假設(shè)A系統(tǒng)接收一個(gè)請(qǐng)求,需要在自己本地寫庫(kù)執(zhí)行SQL,然后需要調(diào)用BCD三個(gè)系統(tǒng)的接口。

假設(shè)自己本地寫庫(kù)要3ms,調(diào)用BCD三個(gè)系統(tǒng)分別要300ms、450ms、200ms。

那么最終請(qǐng)求總延時(shí)是3 + 300 + 450 + 200 = 953ms,接近1s,可能用戶會(huì)感覺(jué)太慢了。

此時(shí)整個(gè)系統(tǒng)大概是這樣的:

但是一旦使用了MQ之后,系統(tǒng)A只需要發(fā)送3條消息到MQ中的3個(gè)消息隊(duì)列,然后就返回給用戶了。

假設(shè)發(fā)送消息到MQ中耗時(shí)20ms,那么用戶感知到這個(gè)接口的耗時(shí)僅僅是20 + 3 = 23ms,用戶幾乎無(wú)感知,倍兒爽!

此時(shí)整個(gè)系統(tǒng)結(jié)構(gòu)大概是這樣的:

可以看到,通過(guò)MQ的異步功能,可以大大提高接口的性能。

解耦

假設(shè)A系統(tǒng)在用戶發(fā)生某個(gè)操作的時(shí)候,需要把用戶提交的數(shù)據(jù)同時(shí)推送到B、C兩個(gè)系統(tǒng)的時(shí)候。

這個(gè)時(shí)候負(fù)責(zé)A系統(tǒng)的哥們想:沒(méi)事啊,B、C兩個(gè)系統(tǒng)給我提供一個(gè)Http接口或者RPC接口,我把數(shù)據(jù)推送過(guò)去不就完事了嗎。負(fù)責(zé)A系統(tǒng)的哥們美滋滋。

如下圖所示:

一切看起來(lái)很美好,但是隨著業(yè)務(wù)快速迭代,這個(gè)時(shí)候系統(tǒng)D也想要這個(gè)數(shù)據(jù)。那既然這樣,A系統(tǒng)的開發(fā)同學(xué)就改咯,在發(fā)送數(shù)據(jù)給BC的同時(shí)加上一個(gè)D。

但是,越到后面越發(fā)現(xiàn),麻煩來(lái)了。。。

整個(gè)系統(tǒng)好像不止這個(gè)數(shù)據(jù)要發(fā)送給BCD、還有第二、第三個(gè)數(shù)據(jù)要發(fā)送給BCD。甚至有時(shí)候又加入了E、F等等系統(tǒng),他們也要這個(gè)數(shù)據(jù)。

并且有時(shí)候可能B系統(tǒng)突然又不要這個(gè)數(shù)據(jù)了,A系統(tǒng)該來(lái)改去,A系統(tǒng)的開發(fā)哥們頭皮發(fā)麻。

更復(fù)雜的場(chǎng)景是,數(shù)據(jù)通過(guò)接口傳給其他系統(tǒng)有時(shí)候還要考慮重試、超時(shí)等一些異常情況,真是頭發(fā)都白了呀。。。

來(lái)看下圖,體會(huì)一下這無(wú)助的現(xiàn)場(chǎng):

這個(gè)時(shí)候,就該我們的MQ粉墨登場(chǎng)了!

這種情況下使用MQ來(lái)解耦是在合適不過(guò)了,因?yàn)樨?fù)責(zé)A系統(tǒng)的哥們只需要把消息扔到MQ就行了,其他系統(tǒng)按需來(lái)訂閱消息就好了。

就算某個(gè)系統(tǒng)不需要這個(gè)數(shù)據(jù)了,也不會(huì)需要A系統(tǒng)改動(dòng)代碼。

看看加入MQ解耦的下圖,是不是清爽了很多!

削峰填谷

舉個(gè)例子,比如我們的訂單系統(tǒng),在下單的時(shí)候就會(huì)往數(shù)據(jù)庫(kù)寫數(shù)據(jù)。但是數(shù)據(jù)庫(kù)只能支撐每秒1000左右的并發(fā)寫入,并發(fā)量再高就容易宕機(jī)。

低峰期的時(shí)候并發(fā)也就100多個(gè),但是在高峰期時(shí)候,并發(fā)量會(huì)突然激增到5000以上,這個(gè)時(shí)候數(shù)據(jù)庫(kù)肯定死了。

如下圖,來(lái)感受一下數(shù)據(jù)庫(kù)被打死的絕望:

但是使用了MQ之后,情況就變了!

消息被MQ保存起來(lái)了,然后系統(tǒng)就可以按照自己的消費(fèi)能力來(lái)消費(fèi),比如每秒1000個(gè)數(shù)據(jù),這樣慢慢寫入數(shù)據(jù)庫(kù),這樣就不會(huì)打死數(shù)據(jù)庫(kù)了:

整個(gè)過(guò)程,如下圖所示:

至于為什么叫做削峰填谷呢?來(lái)看看這個(gè)圖:

如果沒(méi)有用MQ的情況下,并發(fā)量高峰期的時(shí)候是有一個(gè)“頂峰”的,然后高峰期過(guò)后又是一個(gè)低并發(fā)的“谷”。

但是使用了MQ之后,限制消費(fèi)消息的速度為1000,但是這樣一來(lái),高峰期產(chǎn)生的數(shù)據(jù)勢(shì)必會(huì)被積壓在MQ中,高峰就被“削”掉了。

但是因?yàn)橄⒎e壓,在高峰期過(guò)后的一段時(shí)間內(nèi),消費(fèi)消息的速度還是會(huì)維持在1000QPS,直到消費(fèi)完積壓的消息,這就叫做“填谷”

通過(guò)上面的分析,大家就可以知道為什么要使用MQ,以及使用了MQ有什么好處。知其所以然,明白了自己的系統(tǒng)為什么要使用MQ。

這樣以后別人問(wèn)你為啥要用MQ,就不會(huì)出現(xiàn) “我們組長(zhǎng)要用MQ我們就用了” 這樣尷尬的回答了。

使用了MQ之后有什么優(yōu)缺點(diǎn)?

看到這個(gè)問(wèn)題蒙圈了,用了就用了嘛!優(yōu)點(diǎn)上面已經(jīng)說(shuō)了,但是這個(gè)缺點(diǎn)是啥啊。好像沒(méi)啥缺點(diǎn)啊。

如果你這樣想,就大錯(cuò)特錯(cuò)了,在設(shè)計(jì)系統(tǒng)的過(guò)程中,除了要清楚的知道為什么要用這個(gè)東西,還要思考一下用了之后有什么壞處。這樣才能心里有底,防范于未然。

接下來(lái)我們就討論一下,用MQ會(huì)有什么缺點(diǎn)把?

系統(tǒng)可用性降低

大家想想一下,上面的說(shuō)解耦的場(chǎng)景,本來(lái)A系統(tǒng)的哥們要把系統(tǒng)關(guān)鍵數(shù)據(jù)發(fā)送給BC系統(tǒng)的,現(xiàn)在突然加入了一個(gè)MQ了,現(xiàn)在BC系統(tǒng)接收數(shù)據(jù)要通過(guò)MQ來(lái)接收。

但是大家有沒(méi)有考慮過(guò)一個(gè)問(wèn)題,萬(wàn)一MQ掛了怎么辦?這就引出一個(gè)問(wèn)題,加入了MQ之后,系統(tǒng)的可用性是不是就降低了?

因?yàn)槎嗔艘粋€(gè)風(fēng)險(xiǎn)因素:MQ可能會(huì)掛掉。只要MQ掛了,數(shù)據(jù)沒(méi)了,系統(tǒng)運(yùn)行就不對(duì)了。

系統(tǒng)復(fù)雜度提高

本來(lái)我的系統(tǒng)通過(guò)接口調(diào)用一下就能完事的,但是加入一個(gè)MQ之后,需要考慮消息重復(fù)消費(fèi)、消息丟失、甚至消息順序性的問(wèn)題

為了解決這些問(wèn)題,又需要引入很多復(fù)雜的機(jī)制,這樣一來(lái)是不是系統(tǒng)的復(fù)雜度提高了。

數(shù)據(jù)一致性問(wèn)題

本來(lái)好好的,A系統(tǒng)調(diào)用BC系統(tǒng)接口,如果BC系統(tǒng)出錯(cuò)了,會(huì)拋出異常,返回給A系統(tǒng)讓A系統(tǒng)知道,這樣的話就可以做回滾操作了

但是使用了MQ之后,A系統(tǒng)發(fā)送完消息就完事了,認(rèn)為成功了。而剛好C系統(tǒng)寫數(shù)據(jù)庫(kù)的時(shí)候失敗了,但是A認(rèn)為C已經(jīng)成功了?這樣一來(lái)數(shù)據(jù)就不一致了。

通過(guò)分析引入MQ的優(yōu)缺點(diǎn)之后,就明白了使用MQ有很多優(yōu)點(diǎn),但是會(huì)發(fā)現(xiàn)它帶來(lái)的缺點(diǎn)又會(huì)需要你做各種額外的系統(tǒng)設(shè)計(jì)來(lái)彌補(bǔ)

***你可能會(huì)發(fā)現(xiàn)整個(gè)系統(tǒng)復(fù)雜了好幾倍,所以設(shè)計(jì)系統(tǒng)的時(shí)候要基于這些考慮做出取舍,很多時(shí)候你會(huì)發(fā)現(xiàn)該用的還是要用的。。。

怎么保證MQ消息不丟失?

使用了MQ之后,還要關(guān)心消息丟失的問(wèn)題。這里我們挑RabbitMQ來(lái)說(shuō)明一下吧。

生產(chǎn)者弄丟了數(shù)據(jù)

RabbitMQ生產(chǎn)者將數(shù)據(jù)發(fā)送到rabbitmq的時(shí)候,可能數(shù)據(jù)在網(wǎng)絡(luò)傳輸中搞丟了,這個(gè)時(shí)候RabbitMQ收不到消息,消息就丟了。

RabbitMQ提供了兩種方式來(lái)解決這個(gè)問(wèn)題:

事務(wù)方式:

在生產(chǎn)者發(fā)送消息之前,通過(guò)`channel.txSelect`開啟一個(gè)事務(wù),接著發(fā)送消息

如果消息沒(méi)有成功被RabbitMQ接收到,生產(chǎn)者會(huì)收到異常,此時(shí)就可以進(jìn)行事務(wù)回滾`channel.txRollback`然后重新發(fā)送。假如RabbitMQ收到了這個(gè)消息,就可以提交事務(wù)`channel.txCommit`。

但是這樣一來(lái),生產(chǎn)者的吞吐量和性能都會(huì)降低很多,現(xiàn)在一般不這么干。

另外一種方式就是通過(guò)confirm機(jī)制:

這個(gè)confirm模式是在生產(chǎn)者哪里設(shè)置的,就是每次寫消息的時(shí)候會(huì)分配一個(gè)唯一的id,然后RabbitMQ收到之后會(huì)回傳一個(gè)ack,告訴生產(chǎn)者這個(gè)消息ok了。

如果rabbitmq沒(méi)有處理到這個(gè)消息,那么就回調(diào)一個(gè)nack的接口,這個(gè)時(shí)候生產(chǎn)者就可以重發(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ī)制的。

Rabbitmq弄丟了數(shù)據(jù)

RabbitMQ集群也會(huì)弄丟消息,這個(gè)問(wèn)題在官方文檔的教程中也提到過(guò),就是說(shuō)在消息發(fā)送到RabbitMQ之后,默認(rèn)是沒(méi)有落地磁盤的,萬(wàn)一RabbitMQ宕機(jī)了,這個(gè)時(shí)候消息就丟失了。

所以為了解決這個(gè)問(wèn)題,RabbitMQ提供了一個(gè)持久化的機(jī)制,消息寫入之后會(huì)持久化到磁盤

這樣哪怕是宕機(jī)了,恢復(fù)之后也會(huì)自動(dòng)恢復(fù)之前存儲(chǔ)的數(shù)據(jù),這樣的機(jī)制可以確保消息不會(huì)丟失。

設(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ì)將消息持久化到磁盤上去。

但是這樣一來(lái)可能會(huì)有人說(shuō):萬(wàn)一消息發(fā)送到RabbitMQ之后,還沒(méi)來(lái)得及持久化到磁盤就掛掉了,數(shù)據(jù)也丟失了,怎么辦?

對(duì)于這個(gè)問(wèn)題,其實(shí)是配合上面的confirm機(jī)制一起來(lái)保證的,就是在消息持久化到磁盤之后才會(huì)給生產(chǎn)者發(fā)送ack消息。

萬(wàn)一真的遇到了那種極端的情況,生產(chǎn)者是可以感知到的,此時(shí)生產(chǎn)者可以通過(guò)重試發(fā)送消息給別的RabbitMQ節(jié)點(diǎn)

消費(fèi)端弄丟了數(shù)據(jù)

RabbitMQ消費(fèi)端弄丟了數(shù)據(jù)的情況是這樣的:在消費(fèi)消息的時(shí)候,剛拿到消息,結(jié)果進(jìn)程掛了,這個(gè)時(shí)候RabbitMQ就會(huì)認(rèn)為你已經(jīng)消費(fèi)成功了,這條數(shù)據(jù)就丟了。

對(duì)于這個(gè)問(wèn)題,要先說(shuō)明一下RabbitMQ消費(fèi)消息的機(jī)制:在消費(fèi)者收到消息的時(shí)候,會(huì)發(fā)送一個(gè)ack給RabbitMQ,告訴RabbitMQ這條消息被消費(fèi)到了,這樣RabbitMQ就會(huì)把消息刪除。

但是默認(rèn)情況下這個(gè)發(fā)送ack的操作是自動(dòng)提交的,也就是說(shuō)消費(fèi)者一收到這個(gè)消息就會(huì)自動(dòng)返回ack給RabbitMQ,所以會(huì)出現(xiàn)丟消息的問(wèn)題。

所以針對(duì)這個(gè)問(wèn)題的解決方案就是:關(guān)閉RabbitMQ消費(fèi)者的自動(dòng)提交ack,在消費(fèi)者處理完這條消息之后再手動(dòng)提交ack。

這樣即使遇到了上面的情況,RabbitMQ也不會(huì)把這條消息刪除,會(huì)在你程序重啟之后,重新下發(fā)這條消息過(guò)來(lái)。

怎么保證MQ的高可用性性?

使用了MQ之后,我們肯定是希望MQ有高可用特性,因?yàn)椴豢赡芙邮軝C(jī)器宕機(jī)了,就無(wú)法收發(fā)消息的情況。

這一塊我們也是基于RabbitMQ這種經(jīng)典的MQ來(lái)說(shuō)明一下:

RabbitMQ是比較有代表性的,因?yàn)槭腔谥鲝淖龈呖捎眯缘?,我們就以他為例子講解***種MQ的高可用性怎么實(shí)現(xiàn)。

rabbitmq有三種模式:?jiǎn)螜C(jī)模式,普通集群模式,鏡像集群模式

單機(jī)模式

單機(jī)模式就是demo級(jí)別的,就是說(shuō)只有一臺(tái)機(jī)器部署了一個(gè)RabbitMQ程序。

這個(gè)會(huì)存在單點(diǎn)問(wèn)題,宕機(jī)就玩完了,沒(méi)什么高可用性可言。一般就是你本地啟動(dòng)了玩玩兒的,沒(méi)人生產(chǎn)用單機(jī)模式。

普通集群模式

這個(gè)模式的意思就是在多臺(tái)機(jī)器上啟動(dòng)多個(gè)rabbitmq實(shí)例。類似的master-slave模式一樣。

但是創(chuàng)建的queue,只會(huì)放在一個(gè)master rabbtimq實(shí)例上,其他實(shí)例都同步那個(gè)接收消息的RabbitMQ元數(shù)據(jù)。

在消費(fèi)消息的時(shí)候,如果你連接到的RabbitMQ實(shí)例不是存放Queue數(shù)據(jù)的實(shí)例,這個(gè)時(shí)候RabbitMQ就會(huì)從存放Queue數(shù)據(jù)的實(shí)例上拉去數(shù)據(jù),然后返回給客戶端。

總的來(lái)說(shuō),這種方式有點(diǎn)麻煩,沒(méi)有做到真正的分布式,每次消費(fèi)者連接一個(gè)實(shí)例后拉取數(shù)據(jù),如果連接到不是存放queue數(shù)據(jù)的實(shí)例,這個(gè)時(shí)候會(huì)造成額外的性能開銷。如果從放Queue的實(shí)例拉取,會(huì)導(dǎo)致單實(shí)例性能瓶頸。

如果放queue的實(shí)例宕機(jī)了,會(huì)導(dǎo)致其他實(shí)例無(wú)法拉取數(shù)據(jù),這個(gè)集群都無(wú)法消費(fèi)消息了,沒(méi)有做到真正的高可用。

所以這個(gè)事兒就比較尷尬了,這就沒(méi)有什么所謂的高可用性可言了,這方案主要是提高吞吐量的,就是說(shuō)讓集群中多個(gè)節(jié)點(diǎn)來(lái)服務(wù)某個(gè)queue的讀寫操作。

鏡像集群模式

鏡像集群模式才是真正的rabbitmq的高可用模式,跟普通集群模式不一樣的是:創(chuàng)建的queue無(wú)論元數(shù)據(jù)還是queue里的消息都會(huì)存在于多個(gè)實(shí)例上,

每次寫消息到queue的時(shí)候,都會(huì)自動(dòng)把消息到多個(gè)實(shí)例的queue里進(jìn)行消息同步。

這樣的話任何一個(gè)機(jī)器宕機(jī)了別的實(shí)例都可以用提供服務(wù),這樣就做到了真正的高可用了。

但是也存在著不好之處:

  • 性能開銷過(guò)高,消息需要同步所有機(jī)器,會(huì)導(dǎo)致網(wǎng)絡(luò)帶寬壓力和消耗很重
  • 擴(kuò)展性低:無(wú)法解決某個(gè)queue數(shù)據(jù)量特別大的情況,導(dǎo)致queue無(wú)法線性拓展。

就算加了機(jī)器,那個(gè)機(jī)器也會(huì)包含queue的所有數(shù)據(jù),queue的數(shù)據(jù)沒(méi)有做到分布式存儲(chǔ)。

對(duì)于RabbitMQ的高可用一般的做法都是開啟鏡像集群模式,這樣起碼來(lái)說(shuō)做到了高可用,一個(gè)節(jié)點(diǎn)宕機(jī)了,其他節(jié)點(diǎn)可以繼續(xù)提供服務(wù)。

總結(jié)

通過(guò)本篇文章,分析了對(duì)于MQ的一些常規(guī)問(wèn)題:

  • 為什么使用MQ?
  • 使用MQ有什么優(yōu)缺點(diǎn)
  • 如何保證消息不丟失?
  • 如何保證MQ高可用性?

但是,這些問(wèn)題僅僅是使用MQ的其中一部分需要考慮的問(wèn)題,事實(shí)上,還有其他更加復(fù)雜的問(wèn)題需要我們?nèi)ソ鉀Q,

比如:如何保證消息的順序性?消息隊(duì)列如何選型?消息積壓?jiǎn)栴}如何解決?

本文僅僅是針對(duì)RabbitMQ的場(chǎng)景舉例子。還有其他比較的消息隊(duì)列,比如RocketMQ、Kafka。

不同的MQ在面臨上述問(wèn)題的時(shí)候,要根據(jù)他們的原理機(jī)制來(lái)做對(duì)應(yīng)的處理,這些都是本文沒(méi)有顧及的內(nèi)容,將在后面的文章中討論。敬請(qǐng)關(guān)注。

責(zé)任編輯:武曉燕 來(lái)源: 石杉的架構(gòu)筆記
相關(guān)推薦

2019-01-23 17:53:05

程序員技能溝通

2023-06-29 10:10:06

Rocket MQ消息中間件

2023-10-24 07:50:18

消息中間件MQ

2019-11-18 09:58:11

中間件投遞模式

2021-04-22 06:13:41

Express 中間件原理中間件函數(shù)

2020-07-22 14:30:50

程序員財(cái)富螞蟻金服

2015-08-11 11:16:36

淘寶中間件

2022-11-02 10:08:46

分布式高并發(fā)消息中間件

2021-12-14 10:39:12

中間件ActiveMQRabbitMQ

2019-01-29 11:02:30

消息中間件Java互聯(lián)網(wǎng)

2021-06-15 10:01:02

應(yīng)用系統(tǒng)軟件

2019-07-19 07:56:13

消息隊(duì)列消息代理消息中間件

2023-10-16 12:25:48

2017-11-14 11:28:49

程序員索引數(shù)據(jù)

2022-08-09 08:31:29

RocketMQ消息中間件

2023-05-08 08:09:26

路由元信息謂詞

2021-12-16 08:21:31

高并發(fā)消息中間件

2021-07-19 07:55:24

Redux中間件原理

2022-05-10 09:24:44

中間件應(yīng)用方案

2016-11-11 21:00:46

中間件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)