消息中間件的優點
UNIX的進程間通訊就開始運用消息隊列技術,一個進程將數據寫入某個特定的隊列中,其它進程能夠讀取隊列中的數據,從而實現異步通訊。對於現在的分佈式系統,消息隊列已經演變爲獨立的消息中間件產品,相比於RPC同步通訊的方式來講有幾個明顯的優點:編程
- 低耦合,無論是程序仍是模塊之間,使用消息中間件進行間接通訊。
- 消息的順序性,消息隊列能夠保證消息的先進先出。
- 消息可靠傳輸,持久化的存儲使得消息只有在被消費以後纔會刪除。
- 異步通訊能力,相對於RPC來講,異步通訊使得生產者和消費者得以充分執行本身的邏輯而無需等待。
- 緩衝能力,消息中間件像是一個巨大的蓄水池,將高峯期大量的請求存儲下來慢慢交給後臺進行處理,對於秒殺業務來講尤其重要。
可是異步通訊也存在程序設計和編程方面的複雜,同時對於實時性要求較高的業務也不能採用異步通訊,因此要根據業務具體分析。架構
J2EE和JEE是什麼?
J2EE全稱是Java to Enterprise Edition,是一套企業級技術規範,包含:JMS Servlet JSP EJB JPA 等。J2EE發展到1.5版本更名爲JEE5,因此JEE是J2EE規範的延續。 併發
消息中間件的發展歷程
- J2EE時代,消息中間件強調企業級特性,好比消息持久化和事務性要求,所有遵循JMS規範。典型的ActiveMQ、HornetQ,後者如今已經發展成ActiveMQ Artemis子項目,這代表在Java/JEE領域ActiveMQ會繼續發揮不可替代的做用。
- 後Java時代,隨着消息中間件協議AMQP(Advanced Message Queuing Protocol)的誕生,最知名的AMQP消息中間件產品RabbitMQ也隨之誕生,RabbitMQ基於Erlang開發,2007年誕生至今已經稱爲最流行的開源消息中間件產品,ActiveMQ於2013年也開始支持AMQP協議,後來Apache還推出了ActiveMQ Apollo的新項目,也實現了包括AMQP在內的多種協議。
- 互聯網時代,設計思路上採用分佈式系統設計理念,以LinkedIn開源的Kafka爲表明,Kafka使用Scala編寫,因爲良好的水平擴展能力和高性能被普遍採用,同時誕生了一些高仿的相似產品如搜狐的Jafka,阿里的RocketMQ等。
JMS是什麼?
JMS Java Message Service Java消息服務是J2EE架構中針對消息中間件的一組規範。JMS規範定義了Java中訪問消息中間件的接口,但沒有給予實現,具體實現交給消息中間件,稱爲JMS Provider,如ActiveMQ就是一個JMS Provider。簡單說,沒有實現這些接口的消息中間件就不能歸入J2EE(JEE)架構,Java程序也沒法與其進行交互。異步
JMS的兩種消息傳送模型
JMS支持兩種消息傳送模型:點對點消息通訊模型和發佈訂閱模型。分佈式
- 點對點(PTP)消息通訊模型也可稱之爲隊列模式,特定的一條消息只能被一個消費者消費。生產者將消息發送到指定的Queue當中,Broker(中間件)針對消息是否須要持久化進行持久化存儲後通知消費者進行處理,消費者處理完畢後發送一個回執(Acknowledge)給Broker,Broker認爲該消息已被正常消費,因而從持久化存儲中刪除該條消息。回執的發送邏輯內嵌在MQ的API中,無需主動調用。消費者一般能夠經過兩種方式獲取新消息:PUSH和PULL。PUSH方式,由MQ收到消息後主動調用消費者的新消息通知接口,須要消耗MQ寶貴的線程資源,同時消費者只能被動等待消息通知;PULL方式,由消費者輪詢調用MQ API去獲取新消息,對應於ActiveMQ中的方法爲consumer.receive(),不消耗MQ線程,消費者更加主動,雖然消費者的處理邏輯變得稍稍複雜。兩種方式的根本區別在於線程消耗問題,因爲MQ的線程資源相對客戶端更加寶貴,PUSH方式會佔用MQ過多的線程從而難以適應高併發的消息場景。同時當某一消費者離線一段時間再次上線後,大量積壓消息處理會消耗大量MQ線程從而拖累其它消費者的消息處理,因此PULL方式相對來講更好。Kafka已經拋棄了PUSH模式,全面擁抱PULL模式。
- 發佈/訂閱模式(Pub/Sub)也可稱之爲主題模式,特定的一條消息能夠被多個消費者所接收,只要消費者訂閱了某個主題。消息生產者(發佈者)將消息發送到某個稱爲主題(Topic)的虛擬通道中,Topic能夠被多個消費者訂閱,所以該模式相似於廣播的方式。發佈/訂閱模式採用PUSH的方式傳送消息,Subscriber只需保持在線便可。Subscriber分爲臨時性的和持久性的,當Sub離線時,MQ會爲持久性的Sub持久化消息,當Sub恢復時會從新收到消息。可是既然採用Pub/Sub模式就代表容許部分消費者接收不到消息,因此一般會採用臨時性的Subscriber而不是持久性的。