消息隊列之使用場景

1鹏浅、為什么使用消息隊列

如果有人問你這個問題,期望的一個回答是說剂桥,你們公司有個什么業(yè)務(wù)場景,這個業(yè)務(wù)場景有個什么技術(shù)挑戰(zhàn)属提,如果不用 MQ 可能會很麻煩权逗,但是現(xiàn)在用了 MQ 之后帶給了你很多的好處。

消息隊列的使用場景有很多冤议,但是歸根結(jié)底都可以用六字真言來概括:解耦斟薇、異步削峰恕酸。

1.1 解耦

假設(shè)現(xiàn)在有一個系統(tǒng)A堪滨,可以產(chǎn)生userId,系統(tǒng)B和系統(tǒng)C都需要這個userId去做相關(guān)的操作蕊温。

1.jpg

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

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 = "winner";

    public void doSomething() {

        // 系統(tǒng)B和系統(tǒng)C都需要拿著系統(tǒng)A的userId去操作其他的事
        systemB.SystemBNeed2do(userId);
        systemC.SystemCNeed2do(userId);

    }
}

系統(tǒng)上線后袱箱,平穩(wěn)運行了一段時間,一切貌似很完美义矛。

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

于是,系統(tǒng)A的負責人說”好的制轰,那我就不調(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的負責人接了個需求垃杖,也需要用到系統(tǒng)A的userId男杈,于是就跑去跟系統(tǒng)A的負責人說:“老哥,我要用到你的userId调俘,你調(diào)一下我的接口吧”伶棒。

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

2.jpg

然后苞冯,系統(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的負責人,每天都被這給騷擾著梳玫,改來改去爹梁,改來改去.......

還有另外一個問題,調(diào)用系統(tǒng)C的時候提澎,如果系統(tǒng)C掛了姚垃,系統(tǒng)A還得想辦法處理。如果調(diào)用系統(tǒng)D時盼忌,由于網(wǎng)絡(luò)延遲积糯,請求超時了,那系統(tǒng)A是反饋失敗還是重試谦纱?

最后看成,系統(tǒng)A的負責人,覺得隔一段時間就改來改去服协,沒意思绍昂,于是就跑路了……跑路了……了

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

3.jpg

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

  • 系統(tǒng)A只負責把數(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都解耦了乳讥。

1.2 異步

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

4.jpg

代碼如下:

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的接口咬腕。所以,我們可以弄成是這樣的:

5.jpg

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

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

1.3 削峰

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

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

6.jpg

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

2牧挣、副作用

上文列舉了消息隊列的三個用武之地,表面來看醒陆,很多難題只要引入了消息隊列就能夠化腐朽為神奇瀑构。但是,事分兩面刨摩,下面就要說說消息隊列的副作用寺晌。

2.1 高可用問題

系統(tǒng)引入的外部依賴越多,越容易掛掉澡刹。本來你就是 A 系統(tǒng)調(diào)用 BCD 三個系統(tǒng)的接口就好了呻征,人家 ABCD 四個系統(tǒng)好好的,沒啥問題罢浇,你偏加個 MQ 進來陆赋,萬一 MQ 掛了,導致整套系統(tǒng)崩潰的嚷闭,你不就完了攒岛?

7.jpg

為了保證消息隊列的高可用性,一般都會采用集群/分布式的部署方式胞锰。

RabbitMQ 的鏡像集群模式可以保證每個queue存在于多個實例上阵子,每次寫消息到queue的時候,都會自動把消息同步到多個實例胜蛉。

而kafka本來就是天然的分布式消息隊列挠进,由多個broker構(gòu)成集群色乾,一個topic可以劃分為多個partition,每個partition可以存在于不同的broker上领突,每個partition就放一部分數(shù)據(jù)暖璧。也就是說一個topic的數(shù)據(jù),是分散放在多個機器上的君旦,每個機器就放一部分數(shù)據(jù)澎办。

2.2 一致性問題

A 系統(tǒng)處理完了直接返回成功了,調(diào)用方就以為請求就成功了金砍。但是問題是局蚀,假如 BCD 三個系統(tǒng)那里,BD 兩個系統(tǒng)寫庫成功了恕稠,結(jié)果 C 系統(tǒng)寫庫失敗了怎么辦琅绅?會導致數(shù)據(jù)的一致性被破壞。

2.3 復(fù)雜性問題

消息隊列實際是一種非常復(fù)雜的架構(gòu)鹅巍,引入它有很多好處千扶,但是也得針對它帶來的壞處做各種額外的技術(shù)方案和架構(gòu)來規(guī)避掉,比如下面的“奪命三板斧”:

  • 如何保證消息不被重復(fù)消費骆捧?
  • 如何保證消息不丟失澎羞?
  • 如何保證消息的順序投遞?

3敛苇、常見中間件對比

8.png
  • ActiveMQ 的社區(qū)算是比較成熟妆绞,但是較目前來說,ActiveMQ 的性能比較差枫攀,而且版本迭代很慢摆碉,不推薦使用。
  • RabbitMQ 在吞吐量方面雖然稍遜于 Kafka 和 RocketMQ 脓豪,但是由于它基于 erlang 開發(fā)巷帝,所以并發(fā)能力很強,性能極其好扫夜,延時很低楞泼,達到微秒級。但是也因為 RabbitMQ 基于 erlang 開發(fā)笤闯,所以國內(nèi)很少有公司有實力做erlang源碼級別的研究和定制堕阔。如果業(yè)務(wù)場景對并發(fā)量要求不是太高(十萬級、百萬級)颗味,那這四種消息隊列中超陆,RabbitMQ 一定是你的首選。如
  • RocketMQ 阿里出品,Java 系開源項目时呀,源代碼我們可以直接閱讀张漂,然后可以定制自己公司的MQ,并且 RocketMQ 有阿里巴巴的實際業(yè)務(wù)場景的實戰(zhàn)考驗谨娜。RocketMQ 社區(qū)活躍度相對較為一般航攒,不過也還可以,文檔相對來說簡單一些趴梢,然后接口這塊不是按照標準 JMS 規(guī)范走的有些系統(tǒng)要遷移需要修改大量代碼漠畜。
  • kafka 的特點其實很明顯,就是僅僅提供較少的核心功能坞靶,但是提供超高的吞吐量憔狞,ms 級的延遲,極高的可用性以及可靠性彰阴,而且分布式可以任意擴展瘾敢。同時 kafka 最好是支撐較少的 topic 數(shù)量即可,保證其超高吞吐量硝枉。kafka 唯一的一點劣勢是有可能消息重復(fù)消費,那么對數(shù)據(jù)準確性會造成極其輕微的影響倦微,在大數(shù)據(jù)領(lǐng)域中以及日志采集中妻味,這點輕微影響可以忽略這個特性天然適合大數(shù)據(jù)實時計算以及日志收集。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欣福,一起剝皮案震驚了整個濱河市责球,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拓劝,老刑警劉巖雏逾,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異郑临,居然都是意外死亡栖博,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門厢洞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仇让,“玉大人,你說我怎么就攤上這事躺翻∩ミ矗” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵公你,是天一觀的道長踊淳。 經(jīng)常有香客問我,道長陕靠,這世上最難降的妖魔是什么迂尝? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任脱茉,我火速辦了婚禮,結(jié)果婚禮上雹舀,老公的妹妹穿的比我還像新娘芦劣。我一直安慰自己,他們只是感情好说榆,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布虚吟。 她就那樣靜靜地躺著,像睡著了一般签财。 火紅的嫁衣襯著肌膚如雪串慰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天唱蒸,我揣著相機與錄音邦鲫,去河邊找鬼。 笑死神汹,一個胖子當著我的面吹牛庆捺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播屁魏,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼滔以,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了氓拼?” 一聲冷哼從身側(cè)響起你画,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桃漾,沒想到半個月后坏匪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡撬统,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年适滓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恋追。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡粒竖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出几于,到底是詐尸還是另有隱情蕊苗,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布沿彭,位于F島的核電站朽砰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞧柔,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一漆弄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧造锅,春花似錦撼唾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糙箍,卻和暖如春渤愁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背深夯。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工抖格, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咕晋。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓雹拄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掌呜。 傳聞我的和親對象是個殘疾皇子滓玖,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345