• 你可能并不需要消息隊列

    0

     英文原文:You Probably Don’t Need a Message Queue

      我是一個極簡主義者,我不喜歡過早或者沒必要地讓軟件復雜化。而往軟件系統中添加組件就是嚴重增加復雜性的一種做法。我們來拿消息隊列舉個例子。

      消息隊列是一個能讓你獲得容錯性,分布式,解耦等架構能力的系統。紙上談兵的話,它看起來還不錯。

      或許消息列隊在你的應用中有不少適用的場景。你可以看下這篇關于消息隊列優點的文章,看看到底有哪些合適的場景。但可不要因為說"能解耦那太好了”就輕易使用它。我們來看一個例子——你希望你的郵件發送和訂單處理互相解耦。

      因此你發送一個消息到消息隊列里,然后郵件處理系統取出這個消息并發送郵件。那你在一個獨立的單classpath的應用中怎么實現呢?讓你的訂單處理服務依賴于一個郵件服務,然后調用sendEmail()方法,而不是sendToMQ()方法。如果你使用了消息隊列,你需要定義一個兩個系統都能識別的消息格式 ;如果你不使用消息隊列,那么你得定義一個方法簽名。它們有什么本質的區別嗎?其實沒有。

      不過你可能還有別的消費者想要對某個指定的消息進行額外的處理?這的確是可能發生的,而并不只是針對我們這里說到的這個項目而已。盡管確有可能,但相比添加另一個方法調用而言,它可能并不值當。耦合?是的。不過這個耦合并沒有什么不方便的。

      那我應該如何處理峰值流量?你可以通過消息隊列將請求放到一個持久化隊列中,然后再一并處理它們。這是一個非常有用的特性,不過它也受限于幾個 因素——你的請求是在UI后臺處理,還是需要即時響應?serlvet容器的線程池某種程度上可以當作是一個隊列,用戶最終會拿到響應,但是得需要等待(如果線程的超時時間過短的話,請求可能會丟失)。

      你可以使用一個內存隊列來存儲那些較重的請求(得在UI后臺進行處理)。不過注意了,你的隊列并不是默認高可用的。比如說,如果一個消息隊列節點掛掉了,你的消息就丟失了。因此,不去使用應用節點內的內存隊列,而是去使用一個消息隊列,這可能并沒有什么優勢。

      消息隊列使得我們可以進行異步處理——這的確是個有用的特性。你不希望在用戶等待的時候做一些很重的操作。不過你也可以使用一個內存隊列,或者簡單地啟動一個新的線程(比如Spring的@Async注解)。這樣又有另一個問題——如果消息丟失的話是否有問題?如果你應用處理請求的節點掛了,你可以進行恢復嗎?你會發現這事會經常發生,如果不保證所有消息都處理到的話,很難保證功能的正確性。因此,僅將較重的調用進行異步處理是比較可取的。

      把消息放到隊列以便讓另一個組件來進行處理,對于這個場景,如果消息丟失是無法接受的 ,這也有一個很簡單的解決方案——數據庫。你可以把一條processed=false的數據存儲到數據庫中。然后再運行一個調度作業,將所有未處理的記錄挑選出來,異步地進行處理。當處理完成的時候,將標記設為true。我經常用這個方法,包括在一些大型的線上系統中,它也工作得挺好的。

      這樣你還能不斷地對你的應用節點進行擴展,只要它們的內存中沒有任何的持久化狀態的話。不管你是否使用了消息隊列都可以(臨時的內存處理隊列并不屬于持久化狀態)。

      為什么我要給經常用到的消息隊列提供一些備選方案?因為如果你由于不恰當的原因選擇了它,那么消息隊列可能會成為一個負擔。它們并非如想像中那樣容易使用。首先,它有一個學習曲線。一般來說,你集成的組件切分得越多,就越容易出現問題。其次,還有一個設置及配置的成本。比如說,當消息隊列需要在一個集群中運行的話,比如說多個數據中心,那么這就變得復雜了。

      高可用性并不是上來就有的——默認它是不會打開的。還有就是你的應用節點如何連接到消息隊列?通過一個刷新的連接池,或者使用短生命周期的 DNS記錄,還是通過一個負載均衡器?你的隊列可能還有許多配置項,大小是多少,行為是怎樣的(消費者需不需要確認接受,要不要通知處理失敗,多個消費者能夠取到同一個消息嗎,消息有沒有TTL,等等)同時還有網絡及消息傳遞的開銷,尤其是現在大家都喜歡用XML或者JSON來傳輸消息。如果你過度地使用了消息隊列,那么它會增加你系統的延時。

      最后一點,但并不是最次要的——如果出現問題的話,使用消息隊列會讓問題跟蹤變得異常困難。你沒法在IDE中看到所謂的調用層次,因為一旦你發送消息到隊列里了,你就得自己去查找它在哪里處理的了。這可不是聽起來那么簡單的。你看到了吧,它會給你增加許多的復雜性,以及許多需要注意的東西。

      通常而言,在某些上下文中,消息隊列還是非常有用的。當它們的確適合的話,我也會在項目中使用它們——比方說,我們不想丟失消息,但又希望能快速地進行處理。我也見過它在一些不太常見的場景中使用的情況,比如說只有一個應用節點來進行消費,不管是哪個節點投遞過來的消息。你還可以看下 stackoverflow上的這個問題。還有一些使用場景就是,或許你的確需要進行多語言間的通信,又或者你的數據流已經過于復雜了,不使用新的消息消費者而是增加新方法調用的話代價會很大。

      我想說的是那句老掉牙的真理“殺雞焉用牛刀”。如果你不是很確定已經沒有別的更容易管理和維護的方法,一定要使用消息隊列的話,最好不要使用它。不要因為”萬一它有用呢“而去用它——只有你確實覺得需要的話再去使用。因為很有可能,就像這里說到的這個項目一樣,消息隊列其實是沒有必要的。

    來源: deepinmind 

    相似問題

    相關經驗

    相關資訊

    相關文檔

  • sesese色