日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第十二期:面试官问你什么是消息队列?把这篇甩给他!

發(fā)布時間:2023/12/10 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第十二期:面试官问你什么是消息队列?把这篇甩给他! 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

消息隊列不知道大家看到這個詞的時候,會不會覺得它是一個比較高端的技術,反正我是覺得它好像是挺牛逼的。

?

一、什么是消息隊列?

消息隊列不知道大家看到這個詞的時候,會不會覺得它是一個比較高端的技術,反正我是覺得它好像是挺牛逼的。

消息隊列,一般我們會簡稱它為MQ(Message Queue),嗯,就是很直白的簡寫。

我們先不管消息(Message)這個詞,來看看隊列(Queue)。這一看,隊列大家應該都熟悉吧。

隊列是一種先進先出的數(shù)據(jù)結構。

在Java里邊,已經(jīng)實現(xiàn)了不少的隊列了:

那為什么還需要消息隊列(MQ)這種中間件呢???

  • ?到這里,大家可以先猜猜為什么要用消息隊列(MQ)這種中間件,下面會繼續(xù)補充。

消息隊列可以簡單理解為:把要傳輸?shù)臄?shù)據(jù)放在隊列中。

?

科普:

  • ?把數(shù)據(jù)放到消息隊列叫做生產(chǎn)者
  • ?從消息隊列里邊取數(shù)據(jù)叫做消費者

二、為什么要用消息隊列?

為什么要用消息隊列,也就是在問:用了消息隊列有什么好處。我們看看以下的場景

2.1 解耦

現(xiàn)在我有一個系統(tǒng)A,系統(tǒng)A可以產(chǎn)生一個userId

然后,現(xiàn)在有系統(tǒng)B和系統(tǒng)C都需要這個userId去做相關的操作

寫成偽代碼可能是這樣的:

public class SystemA { // 系統(tǒng)B和系統(tǒng)C的依賴 SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); // 系統(tǒng)A獨有的數(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); } }

結構圖如下:

ok,一切平安無事度過了幾個天。

某一天,系統(tǒng)B的負責人告訴系統(tǒng)A的負責人,現(xiàn)在系統(tǒng)B的SystemBNeed2do(String userId)這個接口不再使用了,讓系統(tǒng)A別去調它了。

于是,系統(tǒng)A的負責人說"好的,那我就不調用你了。",于是就把調用系統(tǒng)B接口的代碼給刪掉了:

public void doSomething() { // 系統(tǒng)A不再調用系統(tǒng)B的接口了 //systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); }

又過了幾天,系統(tǒng)D的負責人接了個需求,也需要用到系統(tǒng)A的userId,于是就跑去跟系統(tǒng)A的負責人說:"老哥,我要用到你的userId,你調一下我的接口吧"

于是系統(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獨有的數(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); } }

時間飛逝:

  • ?又過了幾天,系統(tǒng)E的負責人過來了,告訴系統(tǒng)A,需要userId。
  • ?又過了幾天,系統(tǒng)B的負責人過來了,告訴系統(tǒng)A,還是重新掉那個接口吧。
  • ?又過了幾天,系統(tǒng)F的負責人過來了,告訴系統(tǒng)A,需要userId。
  • ?……

于是系統(tǒng)A的負責人,每天都被這給騷擾著,改來改去,改來改去…….

還有另外一個問題,調用系統(tǒng)C的時候,如果系統(tǒng)C掛了,系統(tǒng)A還得想辦法處理。如果調用系統(tǒng)D時,由于網(wǎng)絡延遲,請求超時了,那系統(tǒng)A是反饋fail還是重試??

***,系統(tǒng)A的負責人,覺得隔一段時間就改來改去,沒意思,于是就跑路了。

然后,公司招來一個大佬,大佬經(jīng)過幾天熟悉,上來就說:將系統(tǒng)A的userId寫到消息隊列中,這樣系統(tǒng)A就不用經(jīng)常改動了。為什么呢?下面我們來一起看看:

系統(tǒng)A將userId寫到消息隊列中,系統(tǒng)C和系統(tǒng)D從消息隊列中拿數(shù)據(jù)。這樣有什么好處?

  • ?系統(tǒng)A只負責把數(shù)據(jù)寫到隊列中,誰想要或不想要這個數(shù)據(jù)(消息),系統(tǒng)A一點都不關心。
  • ?即便現(xiàn)在系統(tǒng)D不想要userId這個數(shù)據(jù)了,系統(tǒng)B又突然想要userId這個數(shù)據(jù)了,都跟系統(tǒng)A無關,系統(tǒng)A一點代碼都不用改。
  • ?系統(tǒng)D拿userId不再經(jīng)過系統(tǒng)A,而是從消息隊列里邊拿。系統(tǒng)D即便掛了或者請求超時,都跟系統(tǒng)A無關,只跟消息隊列有關。

這樣一來,系統(tǒng)A與系統(tǒng)B、C、D都解耦了。

2.2 異步

我們再來看看下面這種情況:系統(tǒng)A還是直接調用系統(tǒng)B、C、D

代碼如下:

public class SystemA { SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); SystemD systemD = new SystemD(); // 系統(tǒng)A獨有的數(shù)據(jù) private String userId ; public void doOrder() { // 下訂單 userId = this.order(); // 如果下單成功,則安排其他系統(tǒng)做一些事 systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); systemD.SystemDNeed2do(userId); } }

假設系統(tǒng)A運算出userId具體的值需要50ms,調用系統(tǒng)B的接口需要300ms,調用系統(tǒng)C的接口需要300ms,調用系統(tǒng)D的接口需要300ms。那么這次請求就需要50+300+300+300=950ms

并且我們得知,系統(tǒng)A做的是主要的業(yè)務,而系統(tǒng)B、C、D是非主要的業(yè)務。比如系統(tǒng)A處理的是訂單下單,而系統(tǒng)B是訂單下單成功了,那發(fā)送一條短信告訴具體的用戶此訂單已成功,而系統(tǒng)C和系統(tǒng)D也是處理一些小事而已。

那么此時,為了提高用戶體驗和吞吐量,其實可以異步地調用系統(tǒng)B、C、D的接口。所以,我們可以弄成是這樣的:

系統(tǒng)A執(zhí)行完了以后,將userId寫到消息隊列中,然后就直接返回了(至于其他的操作,則異步處理)。

  • ?本來整個請求需要用950ms(同步)
  • ?現(xiàn)在將調用其他系統(tǒng)接口異步化,從請求到返回只需要100ms(異步)

(例子可能舉得不太好,但我覺得說明到點子上就行了,見諒。)

2.3削峰/限流

我們再來一個場景,現(xiàn)在我們每個月要搞一次大促,大促期間的并發(fā)可能會很高的,比如每秒3000個請求。假設我們現(xiàn)在有兩臺機器處理請求,并且每臺機器只能每次處理1000個請求。

那多出來的1000個請求,可能就把我們整個系統(tǒng)給搞崩了…所以,有一種辦法,我們可以寫到消息隊列中:

系統(tǒng)B和系統(tǒng)C根據(jù)自己的能夠處理的請求數(shù)去消息隊列中拿數(shù)據(jù),這樣即便有每秒有8000個請求,那只是把請求放在消息隊列中,去拿消息隊列的消息由系統(tǒng)自己去控制,這樣就不會把整個系統(tǒng)給搞崩。

三、使用消息隊列有什么問題?

經(jīng)過我們上面的場景,我們已經(jīng)可以發(fā)現(xiàn),消息隊列能做的事其實還是蠻多的。

說到這里,我們先回到文章的開頭,"明明JDK已經(jīng)有不少的隊列實現(xiàn)了,我們還需要消息隊列中間件呢?"其實很簡單,JDK實現(xiàn)的隊列種類雖然有很多種,但是都是簡單的內(nèi)存隊列。為什么我說JDK是簡單的內(nèi)存隊列呢?下面我們來看看要實現(xiàn)消息隊列(中間件)可能要考慮什么問題。

3.1高可用

無論是我們使用消息隊列來做解耦、異步還是削峰,消息隊列肯定不能是單機的。試著想一下,如果是單機的消息隊列,萬一這臺機器掛了,那我們整個系統(tǒng)幾乎就是不可用了。

所以,當我們項目中使用消息隊列,都是得集群/分布式的。要做集群/分布式就必然希望該消息隊列能夠提供現(xiàn)成的支持,而不是自己寫代碼手動去實現(xiàn)。

3.2 數(shù)據(jù)丟失問題

我們將數(shù)據(jù)寫到消息隊列上,系統(tǒng)B和C還沒來得及取消息隊列的數(shù)據(jù),就掛掉了。如果沒有做任何的措施,我們的數(shù)據(jù)就丟了。

學過Redis的都知道,Redis可以將數(shù)據(jù)持久化磁盤上,萬一Redis掛了,還能從磁盤從將數(shù)據(jù)恢復過來。同樣地,消息隊列中的數(shù)據(jù)也需要存在別的地方,這樣才盡可能減少數(shù)據(jù)的丟失。

那存在哪呢?

  • ?磁盤?
  • ?數(shù)據(jù)庫?
  • ?Redis?
  • ?分布式文件系統(tǒng)?

同步存儲還是異步存儲?

3.3消費者怎么得到消息隊列的數(shù)據(jù)?

消費者怎么從消息隊列里邊得到數(shù)據(jù)?有兩種辦法:

  • ?生產(chǎn)者將數(shù)據(jù)放到消息隊列中,消息隊列有數(shù)據(jù)了,主動叫消費者去拿(俗稱push)
  • ?消費者不斷去輪訓消息隊列,看看有沒有新的數(shù)據(jù),如果有就消費(俗稱pull)

3.4其他

除了這些,我們在使用的時候還得考慮各種的問題:

  • ?消息重復消費了怎么辦啊?
  • ?我想保證消息是絕對有順序的怎么做?
  • ?……..

雖然消息隊列給我們帶來了那么多的好處,但同時我們發(fā)現(xiàn)引入消息隊列也會提高系統(tǒng)的復雜性。市面上現(xiàn)在已經(jīng)有不少消息隊列輪子了,每種消息隊列都有自己的特點,選取哪種MQ還得好好斟酌。

***

本文主要講解了什么是消息隊列,消息隊列可以為我們帶來什么好處,以及一個消息隊列可能會涉及到哪些問題。希望給大家?guī)硪欢ǖ膸椭?/p>


閱讀目錄(置頂)(長期更新計算機領域知識)https://blog.csdn.net/weixin_43392489/article/details/102380691

閱讀目錄(置頂)(長期更新計算機領域知識)https://blog.csdn.net/weixin_43392489/article/details/102380882

總結

以上是生活随笔為你收集整理的第十二期:面试官问你什么是消息队列?把这篇甩给他!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。