什么是消息隊列革为?

前言

只有光頭才能變強。

文本已收錄至我的GitHub倉庫琢蛤,歡迎Star:https://github.com/ZhongFuCheng3y/3y

公司用到的很多技術(shù)博其,自己之前都沒學(xué)過()慕淡,于是只能慢慢補了沸毁。這次給大家寫寫我學(xué)習(xí)消息隊列的筆記以清,希望對大家有幫助掷倔。

一勒葱、什么是消息隊列?

消息隊列不知道大家看到這個詞的時候死遭,會不會覺得它是一個比較高端的技術(shù)呀潭,反正我是覺得它好像是挺牛逼的钠署。

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

我們先不管消息(Message)這個詞猾瘸,來看看隊列(Queue)牵触。這一看揽思,隊列大家應(yīng)該都熟悉吧钉汗。

隊列是一種先進先出的數(shù)據(jù)結(jié)構(gòu)。

先進先出

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

Java的隊列實現(xiàn)類

那為什么還需要消息隊列(MQ)這種中間件呢肪凛?伟墙?滴铅?其實這個問題汉匙,跟之前我學(xué)Redis的時候很像。Redis是一個以key-value形式存儲的內(nèi)存數(shù)據(jù)庫守伸,明明我們可以使用類似HashMap這種實現(xiàn)類就可以達到類似的效果了,那還為什么要Redis见芹?《Redis合集

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

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

圖片來源:https://www.cloudamqp.com/blog/2014-12-03-what-is-message-queuing.html

)

科普:

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

二郁惜、為什么要用消息隊列兆蕉?

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

2.1 解耦

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

系統(tǒng)A可以產(chǎn)生一個UserId

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

系統(tǒng)A給系統(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);
        
    }
}

結(jié)構(gòu)圖如下:

結(jié)構(gòu)圖

ok零酪,一切平安無事度過了幾個天四苇。

某一天月腋,系統(tǒng)B的負(fù)責(zé)人告訴系統(tǒng)A的負(fù)責(zé)人榆骚,現(xiàn)在系統(tǒng)B的SystemBNeed2do(String userId)這個接口不再使用了妓肢,讓系統(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é)人接了個需求器腋,也需要用到系統(tǒng)A的userId纫塌,于是就跑去跟系統(tǒng)A的負(fù)責(zé)人說:"老哥措左,我要用到你的userId怎披,你調(diào)一下我的接口吧"

于是系統(tǒng)A說:"沒問題的瓶摆,這就搞"

系統(tǒng)A需要調(diào)用系統(tǒng)D的接口

然后,系統(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的負(fù)責(zé)人過來了菩佑,告訴系統(tǒng)A,需要userId搓劫。
  • 又過了幾天枪向,系統(tǒng)B的負(fù)責(zé)人過來了咧党,告訴系統(tǒng)A傍衡,還是重新掉那個接口吧蛙埂。
  • 又過了幾天绣的,系統(tǒng)F的負(fù)責(zé)人過來了屡江,告訴系統(tǒng)A,需要userId惩嘉。
  • …...

于是系統(tǒng)A的負(fù)責(zé)人宏怔,每天都被這給騷擾著,改來改去斜脂,改來改去.......

還有另外一個問題帚戳,調(diào)用系統(tǒng)C的時候片任,如果系統(tǒng)C掛了对供,系統(tǒng)A還得想辦法處理产场。如果調(diào)用系統(tǒng)D時舞竿,由于網(wǎng)絡(luò)延遲骗奖,請求超時了执桌,那系統(tǒng)A是反饋fail還是重試鼻吮?椎木?

最后香椎,系統(tǒng)A的負(fù)責(zé)人,覺得隔一段時間就改來改去畜伐,沒意思馍惹,于是就跑路了。

然后,公司招來一個大佬万矾,大佬經(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將userId寫到消息隊列中遇西,系統(tǒng)C和系統(tǒng)D從消息隊列中拿數(shù)據(jù)。這樣有什么好處严嗜?

  • 系統(tǒng)A只負(fù)責(zé)把數(shù)據(jù)寫到隊列中粱檀,誰想要或不想要這個數(shù)據(jù)(消息),系統(tǒng)A一點都不關(guān)心漫玄。

    • 即便現(xiàn)在系統(tǒng)D不想要userId這個數(shù)據(jù)了茄蚯,系統(tǒng)B又突然想要userId這個數(shù)據(jù)了,都跟系統(tǒng)A無關(guān)称近,系統(tǒng)A一點代碼都不用改第队。
  • 系統(tǒng)D拿userId不再經(jīng)過系統(tǒng)A哮塞,而是從消息隊列里邊拿刨秆。系統(tǒng)D即便掛了或者請求超時,都跟系統(tǒng)A無關(guān)忆畅,只跟消息隊列有關(guān)衡未。

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

2.2 異步

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

直接調(diào)接口

代碼如下:

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);

    }
}

假設(shè)系統(tǒng)A運算出userId具體的值需要50ms掂之,調(diào)用系統(tǒng)B的接口需要300ms抗俄,調(diào)用系統(tǒng)C的接口需要300ms,調(diào)用系統(tǒng)D的接口需要300ms世舰。那么這次請求就需要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也是處理一些小事而已。

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

此時才用了100ms

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

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

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

2.3削峰/限流

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

削峰的場景

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

寫到消息隊列中,系統(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)幾乎就是不可用了桑李。

萬一單機的隊列掛掉了

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

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

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

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

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

那存在哪呢庶弃?

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

同步存儲還是異步存儲梆造?

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

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

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

3.4其他

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

  • 消息重復(fù)消費了怎么辦袄谜?
  • 我想保證消息是絕對有順序的怎么做础废?
  • ……..

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

最后

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

參考資料:

樂于輸出干貨的Java技術(shù)公眾號:Java3y。公眾號內(nèi)有200多篇原創(chuàng)技術(shù)文章侥钳、海量視頻資源适袜、精美腦圖,不妨來關(guān)注一下舷夺!

帥的人都關(guān)注了

覺得我的文章寫得不錯苦酱,不妨點一下

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末给猾,一起剝皮案震驚了整個濱河市疫萤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敢伸,老刑警劉巖扯饶,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異池颈,居然都是意外死亡尾序,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門躯砰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來每币,“玉大人,你說我怎么就攤上這事琢歇±嫉。” “怎么了梦鉴?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長痕慢。 經(jīng)常有香客問我尚揣,道長,這世上最難降的妖魔是什么掖举? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任快骗,我火速辦了婚禮,結(jié)果婚禮上塔次,老公的妹妹穿的比我還像新娘方篮。我一直安慰自己,他們只是感情好励负,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布藕溅。 她就那樣靜靜地躺著,像睡著了一般继榆。 火紅的嫁衣襯著肌膚如雪巾表。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天略吨,我揣著相機與錄音集币,去河邊找鬼。 笑死翠忠,一個胖子當(dāng)著我的面吹牛鞠苟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秽之,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼当娱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了考榨?” 一聲冷哼從身側(cè)響起跨细,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎董虱,沒想到半個月后扼鞋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡愤诱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捐友。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淫半。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖匣砖,靈堂內(nèi)的尸體忽然破棺而出科吭,到底是詐尸還是另有隱情昏滴,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布对人,位于F島的核電站谣殊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏牺弄。R本人自食惡果不足惜姻几,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望势告。 院中可真熱鬧蛇捌,春花似錦、人聲如沸咱台。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽回溺。三九已至春贸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遗遵,已是汗流浹背萍恕。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓮恭,地道東北人雄坪。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像屯蹦,于是被迫代替她去往敵國和親维哈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 專業(yè)考題類型管理運行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項A選項B選項C選項D選項E選項F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 9,007評論 0 13
  • 案例 有一天登澜,產(chǎn)品跑來說:“我們要做一個用戶注冊功能阔挠,需要在用戶注冊成功后給用戶發(fā)一封成功郵件∧匀洌” 小明(攻城獅)...
    guanguans閱讀 447評論 0 3
  • 《ilua》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 1...
    葉染柒丶閱讀 10,784評論 0 11
  • 一购撼、Python簡介和環(huán)境搭建以及pip的安裝 4課時實驗課主要內(nèi)容 【Python簡介】: Python 是一個...
    _小老虎_閱讀 5,748評論 0 10
  • 文/唐媽 上一章 全部章節(jié) 彈魚兒能跟在牧展元身邊兒自然還是有兩把刷子的,眼見著自己一口濃痰吐在了一雙布鞋上谴仙,抬眼...
    唐媽閱讀 595評論 0 9