消息中間件是一套系統(或平臺),用於應用程序之間進行通訊,系統經過消息傳遞完成交互。數據庫
消息中間件的主要特色有如下幾個。編程
1. 分佈式:消息中間件都是分佈式的,所以才能夠提供異步、解耦等功能。緩存
2. 可靠性:基於消息的通訊是可靠的,消息不會丟失。大多數消息中間件都提供將消息持久化到磁盤的功能。服務器
3. 異步:經過消息中間件,可將遠程同步調用拆解成爲異步調用。對於不須要獲取遠程調用結果的應用場景來講,性能提高明顯。異步
4. 鬆耦合:消息直接由中間件存儲和分發。消息生產者只需關注如何將消息發送給消息中介服務器;消費者只需關注如何從中介服務器訂閱。生產者和消費者之間是徹底解耦的,不須要知道彼此的存在。分佈式
5. 事件驅動:能夠將複雜的應用系統重構成爲事件驅動的系統。事件溯源(Event Sourcing),表示一個對象從建立到消亡,會通過的多種狀態。若是把對象的狀態變化都存儲下來,不但能夠根據狀態變化記錄獲取對象的當前狀態,也能夠回溯對象的變化過程。消息中間件能很好地支持這樣的系統設計方式,將觸發對象狀態變化的事件放入消息隊列。性能
在帶來好處的同時,引入消息中間件也有一些須要注意的地方。spa
1. 分佈式帶來的複雜性:消息中間件都是分佈式的,引入分佈式會大大增長系統複雜度,在不一樣主機、不一樣進程之間的調用和調試,會帶來更多的不穩定性。分佈式系統還會增長對外部系統的依賴。即便本身的系統沒有問題,也可能會由於依賴系統出問題而致使系統不穩定。所以,Martin Fowler曾說:「分佈式調用的第一原則就是不要分佈式。」.net
2. 同步調用應該考慮其餘方式:儘管消息中間件也可用於同步調用,但這並非它的長項,同步調用能夠考慮使用HTTP、NIO等其餘方式。設計
3. 消息中介(Broker):可理解爲消息中間件的服務器。消息中介用於存儲消息,而且維護消息消費者和消息隊列之間的訂閱關係(也可由消費者本身維護)
消息在中介如何存儲,是決定消息中間件功能和性能的最重要因素。目前來講,最主要的兩種存儲消息的方式是kv存儲和順序存儲。後文將詳細介紹兩種不一樣存儲引擎消息中間件的區別。
1. 消息生產者(Producer):與消息消費者一塊兒組成消息中間件的客戶端。生產者用於發送消息到消息中介。
客戶端鏈接服務器通常能夠選擇TCP、HTTP等協議。內網基於長鏈接的TCP協議效率更高,公網能夠考慮HTTP協議穿透防火牆。
2. 消息消費者(Consumer):與消息生產者一塊兒組成消息中間件的客戶端。消費者用於從消息中介得到消息並交給業務系統使用。
消息的消費分紅推送和拉取兩種模式。推送是消息中介主動將消息發送給消息消費者,拉取則是消息消費者主動從消息中介獲取消息。兩種模式的使用場景不太同樣,各有優缺點,下文也會詳細介紹。
爲了便於理解,在這裏將消息中間件和關係型數據庫作一個比較:
1. 消息中介至關於數據庫的服務器;
2. 消息生產者至關於使用INSERT語句的SQL客戶端;
3. 消息消費者至關於使用SELECT語句的SQL客戶端。
固然這個比較不是很是恰當。例如根據實現方式不一樣,消息的刪除可能由消費者發起,也可能由消息中介主動發起。但能比較直觀說明,消息中間件是由服務器和客戶端組成,以及它們所承擔的職責。
JMS
JMS的全稱是Java Message Service,即Java消息服務,定義了Java平臺消息中間件的技術規範。JMS只提供了應用程序對消息中間件操做的接口規範,並未提供實現,其實現由各個消息中間件廠商的驅動程序來提供,和Java的另外一個規範標準JDBC類似。遵循JMS規範的消息中間件都使用統一的API。
JMS定義了消息的編程模型,如鏈接工廠、會話、消息目的地、消息生產者、消息消費者、消息體、消息優先級和消息類型等。本文並不會詳細介紹JMS,有興趣瞭解請查閱相關資料。但會重點介紹JMS中定義的消息類型,由於後面介紹的幾種消息中間件如何支持消息類型是本文的關鍵之一。
消息類型分爲點對點和發佈/訂閱兩種。
1. 點對點(Point To Point):消息生產者將消息發送到消息隊列(Queue)中,只有一個消費者可以消費此消息,消費完成以後消息即刪除。
這裏應該注意的是,任意一個消費者均可以消費這個消息,但消息絕對不會被兩個消費者重複消費。
消息的消費者和生產者沒有時間依賴,能夠先發送消息,再啓動消費者。
圖2展現了JMS的點對點消息類型。
2. 發佈/訂閱(Publish/Subscribe):消息生產者將消息發送到消息主題(Topic)中,全部訂閱這個主題的消費者均可以消費此消息,當全部訂閱者都消費完成以後才能刪除消息。
消息的生產者和消費者之間有時間依賴,只有事先訂閱這個主題的消費者纔可消費。若是先發送消息,後訂閱主題,那麼訂閱以前的消息將不能被這個訂閱者消費。
訂閱者又可分爲持久化訂閱和非持久化訂閱。若是持久化的訂閱者在訂閱以後離線,收到的消息仍會在訂閱者再次上線時收到,不會錯過消息。而非持久化的訂閱者一旦離線,離線時的消息將被錯過。
圖3展現了JMS的發佈/訂閱消息類型。
最後,須要介紹消息傳遞語義(Message Delivery Semantics)。消息傳遞的擔保有3種級別:最多一次(At Most Once),至少一次(At Least Once)和精確地僅發送一次(Exactly Once)。
【最多一次】
消息只發送或消費一次,不管消息中介是否收到消息,或者消息是否已消費成功,都不會再次發送。
這樣作的問題是,消息可能會丟失。雖然客戶端發送了消息,但消息中介還來不及存儲就崩潰,那麼這條消息就會丟失。
【至少一次】
若是消息中介沒有明確告訴客戶端消息已經收到,那麼客戶端會從新發送或消費這條消息。
這樣作的問題是,消息可能會重複發送或消費。例如消息已經存到了消息中介,但還沒來得及給客戶端發送確認信息,消息中介就崩潰了。那麼等消息中介從新啓動以後,客戶端會從新發送這條消息,形成重複。
【精確地僅發送一次】
既不會多也不會少發送消息。通常經過事務來實現,只有消息中介收到客戶端確認處理成功的信息,纔會提交事務,不然在通過必定時間限制以後消息會回滾。精確地僅發送一次消息,不會丟失也不會有重複的消息,但要達到這一點,對性能的損耗很是大。
JMS要求消息精確地僅發送一次。
應用場景
【業務解耦】
各個業務系統僅須要處理本身的業務邏輯,而且發送事件消息到消息中間件。下游業務系統直接訂閱消息中間件的隊列或主題獲取事件。這樣不但解耦了系統間的依賴,並且使調用異步化,提高了系統的性能。圖4顯示瞭如何經過消息中間件將訂單系統解耦。
【削峯填谷】
若是上游系統的吞吐能力強於下游系統,那麼在上游系統滿負荷時將沖垮下游系統。使用消息中間件的定時定量推送或者定時定量拉取,可在上游峯值時堆積消息,在峯值過去時慢慢消費,加強系統的緩衝能力。圖5顯示瞭如何經過消息中間件緩衝業務洪峯,而且使用定時定量分流消費消息。
【廣播通知】
系統一個狀態的改變,須要通知多個相關係統,可經過消息訂閱的方式推送給各個訂閱者系統。好比數據庫值的改變,須要通知全部的緩存系統更新,能夠把數據庫值改變發送消息給消息中間件,而後各緩存訂閱相關主題,收到消息後更新本身的緩存。圖6顯示了數據中心如何經過廣播通知已訂閱的緩存系統更新數據。
【日誌分析】
日誌分析每每須要處理大量日誌,不可能存儲在一臺物理機上。消息中間件可提供一個集羣,用來存儲海量消息,將其緩存到消息中間件。比較經常使用的日誌分析系統是使用日誌收集組件(如Flume)收集,存儲到高吞吐量的消息中間件(如Kafka),供實時分析系統(如Storm)分析日誌。圖7描述了日誌分析系統的基本組成部分。
消息中間件概覽
表1展現了目前比較流行的消息中間件的簡單對比,但願對讀者的選型有所幫助。