標籤:
我是一個極簡主義者,我不喜歡過早或者沒必要地讓軟體複雜化。而往軟體系統中添加組件就是嚴重增加複雜性的一種做法。我們來拿訊息佇列舉個例子。
訊息佇列是一個能讓你獲得容錯性,分布式,解耦等架構能力的系統。紙上談兵的話,它看起來還不錯。
或許訊息列隊在你的應用中有不少適用的情境。你可以看下這篇關於訊息佇列優點的文章,看看到底有哪些合適的情境。但可不要因為說"能解耦那太好了”就輕易使用它。我們來看一個例子——你希望你的郵件發送和訂單處理互相解耦。
因此你發送一個訊息到訊息佇列裡,然後郵件處理系統取出這個訊息並發送郵件。那你在一個獨立的單classpath的應用中怎麼實現呢?讓你的訂單處理服務依賴於一個郵件服務,然後調用sendEmail()方法,而不是sendToMQ()方法。如果你使用了訊息佇列,你需要定義一個兩個系統都能識別的訊息格式 ;如果你不使用訊息佇列,那麼你得定義一個方法簽名。它們有什麼本質的區別嗎?其實沒有。
不過你可能還有別的消費者想要對某個指定的訊息進行額外的處理?這的確是可能發生的,而並不只是針對我們這裡說到的這個項目而已。儘管確有可能,但相比添加另一個方法調用而言,它可能並不值當。耦合?是的。不過這個耦合并沒有什麼不方便的。
那我應該如何處理峰值流量?你可以通過訊息佇列將請求放到一個持久化隊列中,然後再一併處理它們。這是一個非常有用的特性,不過它也受限於幾個 因素——你的請求是在UI幕後處理,還是需要即時響應?serlvet容器的線程池某種程度上可以當作是一個隊列,使用者最終會拿到響應,但是得需要等待(如果線程的逾時時間過短的話,請求可能會丟失)。
你可以使用一個記憶體隊列來儲存那些較重的請求(得在UI後台進行處理)。不過注意了,你的隊列並不是預設高可用的。比如說,如果一個訊息佇列節點掛掉了,你的訊息就丟失了。因此,不去使用應用節點內的記憶體隊列,而是去使用一個訊息佇列,這可能並沒有什麼優勢。
訊息佇列使得我們可以進行非同步處理——這的確是個有用的特性。你不希望在使用者等待的時候做一些很重的操作。不過你也可以使用一個記憶體隊列,或者簡單地啟動一個新的線程(比如Spring的@Async註解)。這樣又有另一個問題——如果訊息丟失的話是否有問題?如果你應用處理請求的節點掛了,你可以進行恢複嗎?你會發現這事會經常發生,如果不保證所有訊息都處理到的話,很難保證功能的正確性。因此,僅將較重的調用進行非同步處理是比較可取的。
把訊息放到隊列以便讓另一個組件來進行處理,對於這個情境,如果訊息丟失是無法接受的 ,這也有一個很簡單的解決方案——資料庫。你可以把一條processed=false的資料存放區到資料庫中。然後再運行一個調度作業,將所有未處理的記錄挑選出來,非同步地進行處理。當處理完成的時候,將標記設為true。我經常用這個方法,包括在一些大型的線上系統中,它也工作得挺好的。
這樣你還能不斷地對你的應用節點進行擴充,只要它們的記憶體中沒有任何的持久化狀態的話。不管你是否使用了訊息佇列都可以(臨時的記憶體處理隊列並不屬於持久化狀態)。
為什麼我要給經常用到的訊息佇列提供一些備選方案?因為如果你由於不恰當的原因選擇了它,那麼訊息佇列可能會成為一個負擔。它們並非如想像中那樣容易使用。首先,它有一個學習曲線。一般來說,你整合的組件切分得越多,就越容易出現問題。其次,還有一個設定及配置的成本。比如說,當訊息佇列需要在一個叢集中啟動並執行話,比如說多個資料中心,那麼這就變得複雜了。
高可用性並不是上來就有的——預設它是不會開啟的。還有就是你的應用節點如何串連到訊息佇列?通過一個重新整理的串連池,或者使用短生命週期的DNS記錄,還是通過一個負載平衡器?你的隊列可能還有許多配置項,大小是多少,行為是怎樣的(消費者需不需要確認接受,要不要通知處理失敗,多個消費者能夠取到同一個訊息嗎,訊息有沒有TTL,等等)同時還有網路及訊息傳遞的開銷,尤其是現在大家都喜歡用XML或者JSON來傳輸訊息。如果你過度地使用了訊息佇列,那麼它會增加你系統的延時。
最後一點,但並不是最次要的——如果出現問題的話,使用訊息佇列會讓問題跟蹤變得異常困難。你沒法在IDE中看到所謂的調用層次,因為一旦你發送訊息到隊列裡了,你就得自己去尋找它在哪裡處理的了。這可不是聽起來那麼簡單的。你看到了吧,它會給你增加許多的複雜性,以及許多需要注意的東西。
通常而言,在某些上下文中,訊息佇列還是非常有用的。當它們的確適合的話,我也會在項目中使用它們——比方說,我們不想丟失訊息,但又希望能快速地進行處理。我也見過它在一些不太常見的情境中使用的情況,比如說只有一個應用節點來進行消費,不管是哪個節點投遞過來的訊息。你還可以看下stackoverflow上的這個問題。還有一些使用情境就是,或許你的確需要進行多語言間的通訊,又或者你的資料流已經過於複雜了,不使用新的訊息消費者而是增加新方法調用的話代價會很大。
我想說的是那句老掉牙的真理“殺雞焉用牛刀”。如果你不是很確定已經沒有別的更容易管理和維護的方法,一定要使用訊息佇列的話,最好不要使用它。不要因為”萬一它有用呢“而去用它——只有你確實覺得需要的話再去使用。因為很有可能,就像這裡說到的這個項目一樣,訊息佇列其實是沒有必要的。
英文原文:You Probably Don’t Need a Message Queue
【轉】你可能並不需要訊息佇列