摘要:系統(tǒng)拿不再經(jīng)過系統(tǒng),而是從消息隊(duì)列里邊拿。高可用無論是我們使用消息隊(duì)列來做解耦異步還是削峰,消息隊(duì)列肯定不能是單機(jī)的。最后本文主要講解了什么是消息隊(duì)列,消息隊(duì)列可以為我們帶來什么好處,以及一個(gè)消息隊(duì)列可能會(huì)涉及到哪些問題。
前言
只有光頭才能變強(qiáng)。文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongFuCheng3y/3y
公司用到的很多技術(shù),自己之前都沒學(xué)過(尬),于是只能慢慢補(bǔ)了。這次給大家寫寫我學(xué)習(xí)消息隊(duì)列的筆記,希望對(duì)大家有幫助。
一、什么是消息隊(duì)列?消息隊(duì)列不知道大家看到這個(gè)詞的時(shí)候,會(huì)不會(huì)覺得它是一個(gè)比較高端的技術(shù),反正我是覺得它好像是挺牛逼的。
消息隊(duì)列,一般我們會(huì)簡(jiǎn)稱它為MQ(Message Queue),嗯,就是很直白的簡(jiǎn)寫。
我們先不管消息(Message)這個(gè)詞,來看看隊(duì)列(Queue)。這一看,隊(duì)列大家應(yīng)該都熟悉吧。
隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。
在Java里邊,已經(jīng)實(shí)現(xiàn)了不少的隊(duì)列了:
那為什么還需要消息隊(duì)列(MQ)這種中間件呢???其實(shí)這個(gè)問題,跟之前我學(xué)Redis的時(shí)候很像。Redis是一個(gè)以key-value形式存儲(chǔ)的內(nèi)存數(shù)據(jù)庫,明明我們可以使用類似HashMap這種實(shí)現(xiàn)類就可以達(dá)到類似的效果了,那還為什么要Redis?《Redis合集》
到這里,大家可以先猜猜為什么要用消息隊(duì)列(MQ)這種中間件,下面會(huì)繼續(xù)補(bǔ)充。
消息隊(duì)列可以簡(jiǎn)單理解為:把要傳輸?shù)臄?shù)據(jù)放在隊(duì)列中。
)
科普:
把數(shù)據(jù)放到消息隊(duì)列叫做生產(chǎn)者
從消息隊(duì)列里邊取數(shù)據(jù)叫做消費(fèi)者
二、為什么要用消息隊(duì)列?為什么要用消息隊(duì)列,也就是在問:用了消息隊(duì)列有什么好處。我們看看以下的場(chǎng)景
2.1 解耦現(xiàn)在我有一個(gè)系統(tǒng)A,系統(tǒng)A可以產(chǎn)生一個(gè)userId
然后,現(xiàn)在有系統(tǒng)B和系統(tǒng)C都需要這個(gè)userId去做相關(guān)的操作
寫成偽代碼可能是這樣的:
public class SystemA { // 系統(tǒng)B和系統(tǒng)C的依賴 SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); // 系統(tǒng)A獨(dú)有的數(shù)據(jù)userId private String userId = "Java3y"; public void doSomething() { // 系統(tǒng)B和系統(tǒng)C都需要拿著系統(tǒng)A的userId去操作其他的事 systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); } }
結(jié)構(gòu)圖如下:
ok,一切平安無事度過了幾個(gè)天。
某一天,系統(tǒng)B的負(fù)責(zé)人告訴系統(tǒng)A的負(fù)責(zé)人,現(xiàn)在系統(tǒng)B的SystemBNeed2do(String userId)這個(gè)接口不再使用了,讓系統(tǒng)A別去調(diào)它了。
于是,系統(tǒng)A的負(fù)責(zé)人說"好的,那我就不調(diào)用你了。",于是就把調(diào)用系統(tǒng)B接口的代碼給刪掉了:
public void doSomething() { // 系統(tǒng)A不再調(diào)用系統(tǒng)B的接口了 //systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); }
又過了幾天,系統(tǒng)D的負(fù)責(zé)人接了個(gè)需求,也需要用到系統(tǒng)A的userId,于是就跑去跟系統(tǒng)A的負(fù)責(zé)人說:"老哥,我要用到你的userId,你調(diào)一下我的接口吧"
于是系統(tǒng)A說:"沒問題的,這就搞"
然后,系統(tǒng)A的代碼如下:
public class SystemA { // 已經(jīng)不再需要系統(tǒng)B的依賴了 // SystemB systemB = new SystemB(); // 系統(tǒng)C和系統(tǒng)D的依賴 SystemC systemC = new SystemC(); SystemD systemD = new SystemD(); // 系統(tǒng)A獨(dú)有的數(shù)據(jù) private String userId = "Java3y"; public void doSomething() { // 已經(jīng)不再需要系統(tǒng)B的依賴了 //systemB.SystemBNeed2do(userId); // 系統(tǒng)C和系統(tǒng)D都需要拿著系統(tǒng)A的userId去操作其他的事 systemC.SystemCNeed2do(userId); systemD.SystemDNeed2do(userId); } }
時(shí)間飛逝:
又過了幾天,系統(tǒng)E的負(fù)責(zé)人過來了,告訴系統(tǒng)A,需要userId。
又過了幾天,系統(tǒng)B的負(fù)責(zé)人過來了,告訴系統(tǒng)A,還是重新掉那個(gè)接口吧。
又過了幾天,系統(tǒng)F的負(fù)責(zé)人過來了,告訴系統(tǒng)A,需要userId。
…...
于是系統(tǒng)A的負(fù)責(zé)人,每天都被這給騷擾著,改來改去,改來改去.......
還有另外一個(gè)問題,調(diào)用系統(tǒng)C的時(shí)候,如果系統(tǒng)C掛了,系統(tǒng)A還得想辦法處理。如果調(diào)用系統(tǒng)D時(shí),由于網(wǎng)絡(luò)延遲,請(qǐng)求超時(shí)了,那系統(tǒng)A是反饋fail還是重試??
最后,系統(tǒng)A的負(fù)責(zé)人,覺得隔一段時(shí)間就改來改去,沒意思,于是就跑路了。
然后,公司招來一個(gè)大佬,大佬經(jīng)過幾天熟悉,上來就說:將系統(tǒng)A的userId寫到消息隊(duì)列中,這樣系統(tǒng)A就不用經(jīng)常改動(dòng)了。為什么呢?下面我們來一起看看:
系統(tǒng)A將userId寫到消息隊(duì)列中,系統(tǒng)C和系統(tǒng)D從消息隊(duì)列中拿數(shù)據(jù)。這樣有什么好處?
系統(tǒng)A只負(fù)責(zé)把數(shù)據(jù)寫到隊(duì)列中,誰想要或不想要這個(gè)數(shù)據(jù)(消息),系統(tǒng)A一點(diǎn)都不關(guān)心。
即便現(xiàn)在系統(tǒng)D不想要userId這個(gè)數(shù)據(jù)了,系統(tǒng)B又突然想要userId這個(gè)數(shù)據(jù)了,都跟系統(tǒng)A無關(guān),系統(tǒng)A一點(diǎn)代碼都不用改。
系統(tǒng)D拿userId不再經(jīng)過系統(tǒng)A,而是從消息隊(duì)列里邊拿。系統(tǒng)D即便掛了或者請(qǐng)求超時(shí),都跟系統(tǒng)A無關(guān),只跟消息隊(duì)列有關(guān)。
這樣一來,系統(tǒng)A與系統(tǒng)B、C、D都解耦了。
2.2 異步我們?cè)賮砜纯聪旅孢@種情況:系統(tǒng)A還是直接調(diào)用系統(tǒng)B、C、D
代碼如下:
public class SystemA { SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); SystemD systemD = new SystemD(); // 系統(tǒng)A獨(dú)有的數(shù)據(jù) private String userId ; public void doOrder() { // 下訂單 userId = this.order(); // 如果下單成功,則安排其他系統(tǒng)做一些事 systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); systemD.SystemDNeed2do(userId); } }
假設(shè)系統(tǒng)A運(yùn)算出userId具體的值需要50ms,調(diào)用系統(tǒng)B的接口需要300ms,調(diào)用系統(tǒng)C的接口需要300ms,調(diào)用系統(tǒng)D的接口需要300ms。那么這次請(qǐng)求就需要50+300+300+300=950ms
并且我們得知,系統(tǒng)A做的是主要的業(yè)務(wù),而系統(tǒng)B、C、D是非主要的業(yè)務(wù)。比如系統(tǒng)A處理的是訂單下單,而系統(tǒng)B是訂單下單成功了,那發(fā)送一條短信告訴具體的用戶此訂單已成功,而系統(tǒng)C和系統(tǒng)D也是處理一些小事而已。
那么此時(shí),為了提高用戶體驗(yàn)和吞吐量,其實(shí)可以異步地調(diào)用系統(tǒng)B、C、D的接口。所以,我們可以弄成是這樣的:
系統(tǒng)A執(zhí)行完了以后,將userId寫到消息隊(duì)列中,然后就直接返回了(至于其他的操作,則異步處理)。
本來整個(gè)請(qǐng)求需要用950ms(同步)
現(xiàn)在將調(diào)用其他系統(tǒng)接口異步化,只需要100ms(異步)
(例子可能舉得不太好,但我覺得說明到點(diǎn)子上就行了,見諒。)
2.3削峰/限流我們?cè)賮硪粋€(gè)場(chǎng)景,現(xiàn)在我們每個(gè)月要搞一次大促,大促期間的并發(fā)可能會(huì)很高的,比如每秒3000個(gè)請(qǐng)求。假設(shè)我們現(xiàn)在有兩臺(tái)機(jī)器處理請(qǐng)求,并且每臺(tái)機(jī)器只能每次處理1000個(gè)請(qǐng)求。
那多出來的1000個(gè)請(qǐng)求,可能就把我們整個(gè)系統(tǒng)給搞崩了...所以,有一種辦法,我們可以寫到消息隊(duì)列中:
系統(tǒng)B和系統(tǒng)C根據(jù)自己的能夠處理的請(qǐng)求數(shù)去消息隊(duì)列中拿數(shù)據(jù),這樣即便有每秒有8000個(gè)請(qǐng)求,那只是把請(qǐng)求放在消息隊(duì)列中,去拿消息隊(duì)列的消息由系統(tǒng)自己去控制,這樣就不會(huì)把整個(gè)系統(tǒng)給搞崩。
三、使用消息隊(duì)列有什么問題?經(jīng)過我們上面的場(chǎng)景,我們已經(jīng)可以發(fā)現(xiàn),消息隊(duì)列能做的事其實(shí)還是蠻多的。
說到這里,我們先回到文章的開頭,"明明JDK已經(jīng)有不少的隊(duì)列實(shí)現(xiàn)了,我們還需要消息隊(duì)列中間件呢?"其實(shí)很簡(jiǎn)單,JDK實(shí)現(xiàn)的隊(duì)列種類雖然有很多種,但是都是簡(jiǎn)單的內(nèi)存隊(duì)列。為什么我說JDK是簡(jiǎn)單的內(nèi)存隊(duì)列呢?下面我們來看看要實(shí)現(xiàn)消息隊(duì)列(中間件)可能要考慮什么問題。
3.1高可用無論是我們使用消息隊(duì)列來做解耦、異步還是削峰,消息隊(duì)列肯定不能是單機(jī)的。試著想一下,如果是單機(jī)的消息隊(duì)列,萬一這臺(tái)機(jī)器掛了,那我們整個(gè)系統(tǒng)幾乎就是不可用了。
所以,當(dāng)我們項(xiàng)目中使用消息隊(duì)列,都是得集群/分布式的。要做集群/分布式就必然希望該消息隊(duì)列能夠提供現(xiàn)成的支持,而不是自己寫代碼手動(dòng)去實(shí)現(xiàn)。
3.2 數(shù)據(jù)丟失問題我們將數(shù)據(jù)寫到消息隊(duì)列上,系統(tǒng)B和C還沒來得及取消息隊(duì)列的數(shù)據(jù),就掛掉了。如果沒有做任何的措施,我們的數(shù)據(jù)就丟了。
學(xué)過Redis的都知道,Redis可以將數(shù)據(jù)持久化磁盤上,萬一Redis掛了,還能從磁盤從將數(shù)據(jù)恢復(fù)過來。同樣地,消息隊(duì)列中的數(shù)據(jù)也需要存在別的地方,這樣才盡可能減少數(shù)據(jù)的丟失。
那存在哪呢?
磁盤?
數(shù)據(jù)庫?
Redis?
分布式文件系統(tǒng)?
同步存儲(chǔ)還是異步存儲(chǔ)?
3.3消費(fèi)者怎么得到消息隊(duì)列的數(shù)據(jù)?消費(fèi)者怎么從消息隊(duì)列里邊得到數(shù)據(jù)?有兩種辦法:
生產(chǎn)者將數(shù)據(jù)放到消息隊(duì)列中,消息隊(duì)列有數(shù)據(jù)了,主動(dòng)叫消費(fèi)者去拿(俗稱push)
消費(fèi)者不斷去輪訓(xùn)消息隊(duì)列,看看有沒有新的數(shù)據(jù),如果有就消費(fèi)(俗稱pull)
3.4其他除了這些,我們?cè)?strong>使用的時(shí)候還得考慮各種的問題:
消息重復(fù)消費(fèi)了怎么辦?。?/p>
我想保證消息是絕對(duì)有順序的怎么做?
……..
雖然消息隊(duì)列給我們帶來了那么多的好處,但同時(shí)我們發(fā)現(xiàn)引入消息隊(duì)列也會(huì)提高系統(tǒng)的復(fù)雜性。市面上現(xiàn)在已經(jīng)有不少消息隊(duì)列輪子了,每種消息隊(duì)列都有自己的特點(diǎn),選取哪種MQ還得好好斟酌。
最后本文主要講解了什么是消息隊(duì)列,消息隊(duì)列可以為我們帶來什么好處,以及一個(gè)消息隊(duì)列可能會(huì)涉及到哪些問題。希望給大家?guī)硪欢ǖ膸椭?/p>
參考資料:
Kafka簡(jiǎn)明教程
https://zhuanlan.zhihu.com/p/37405836
消息隊(duì)列使用的四種場(chǎng)景介紹,有圖有解析,一看就懂
https://zhuanlan.zhihu.com/p/55712984
消息隊(duì)列設(shè)計(jì)精要
https://zhuanlan.zhihu.com/p/21479556
消息隊(duì)列的使用場(chǎng)景是怎樣的
https://www.zhihu.com/question/34243607
樂于輸出干貨的Java技術(shù)公眾號(hào):Java3y。公眾號(hào)內(nèi)有200多篇原創(chuàng)技術(shù)文章、海量視頻資源、精美腦圖,不妨來關(guān)注一下!
覺得我的文章寫得不錯(cuò),不妨點(diǎn)一下贊!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77567.html
摘要:我在前面的文章中也提到了應(yīng)該怎么做自我介紹與項(xiàng)目介紹,詳情可以查看這篇文章備戰(zhàn)春招秋招系列初出茅廬的程序員該如何準(zhǔn)備面試。因此基于事件消息對(duì)象驅(qū)動(dòng)的業(yè)務(wù)架構(gòu)可以是一系列流程。 showImg(https://user-gold-cdn.xitu.io/2018/11/14/16711ac29c2ae52c?w=928&h=531&f=png&s=798562); 一 消息隊(duì)列MQ的...
摘要:交換器和隊(duì)列是綁定的,用于消息隊(duì)列和交換器之間的關(guān)聯(lián)。會(huì)將消息發(fā)送到匹配的隊(duì)列中去使用號(hào)和號(hào)。參考資料消息隊(duì)列之什么是消息隊(duì)列淺談消息隊(duì)列及常見的消息中間件 一、定義 比較官方的說法是是指利用 高效可靠 的 消息傳遞機(jī)制 進(jìn)行與平臺(tái)無關(guān)的 數(shù)據(jù)交流,并基于 數(shù)據(jù)通信 來進(jìn)行分布式系統(tǒng)的集成。我們可以理解為將要傳輸?shù)臄?shù)據(jù)放在一個(gè)隊(duì)列中。 二、為什么使用消息隊(duì)列 1、解耦(1)發(fā)送者和接收...
摘要:通過以上分析我們可以得出消息隊(duì)列具有很好的削峰作用的功能即通過異步處理,將短時(shí)間高并發(fā)產(chǎn)生的事務(wù)消息存儲(chǔ)在消息隊(duì)列中,從而削平高峰期的并發(fā)事務(wù)。 該文已加入開源項(xiàng)目:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識(shí)的文檔類項(xiàng)目,Star 數(shù)接近 16k)。地址:https://github.com/Snailclimb... 本文內(nèi)容思維導(dǎo)圖:showImg(ht...
摘要:后續(xù)介紹交換機(jī),生產(chǎn)者直接將消息投遞到中。消息,服務(wù)器和應(yīng)用程序之間傳送的數(shù)據(jù),由和組成。也稱為消息隊(duì)列,保存消息并將它們轉(zhuǎn)發(fā)給消費(fèi)者。主要是應(yīng)為和有一個(gè)綁定的關(guān)系。 showImg(https://img-blog.csdnimg.cn/20190509221741422.gif); showImg(https://img-blog.csdnimg.cn/20190731191914...
閱讀 3527·2023-04-25 14:57
閱讀 2574·2021-11-22 14:56
閱讀 2097·2021-09-29 09:45
閱讀 1779·2021-09-22 15:53
閱讀 3327·2021-08-25 09:41
閱讀 908·2019-08-29 15:22
閱讀 3307·2019-08-29 13:22
閱讀 3132·2019-08-29 13:08