1. 什么是JMS
JMS即Java消息服務(wù)(Java Message Service)應(yīng)用程序接口,是一個(gè)Java平臺(tái)中關(guān)于面向消息中間件(MOM)的API,用于在兩個(gè)應(yīng)用程序之間,或分布式系統(tǒng)中發(fā)送消息,進(jìn)行異步通信。Java消息服務(wù)是一個(gè)與具體平臺(tái)無(wú)關(guān)的API,絕大多數(shù)MOM提供商都對(duì)JMS提供支持(百度百科給出的概述)。我們可以簡(jiǎn)單的理解:兩個(gè)應(yīng)用程序之間需要進(jìn)行通信,我們使用一個(gè)JMS服務(wù),進(jìn)行中間的轉(zhuǎn)發(fā),通過(guò)JMS 的使用,我們可以解除兩個(gè)程序之間的耦合。
2. JMS的優(yōu)勢(shì)
- Asynchronous(異步)
JMS is asynchronous by default. So to receive a message, the client is not required to send the request. The message will arrive automatically to the client as they become available.(JMS 原本就是一個(gè)異步的消息服務(wù),客戶(hù)端獲取消息的時(shí)候,不需要主動(dòng)發(fā)送請(qǐng)求,消息會(huì)自動(dòng)發(fā)送給可用的客戶(hù)端)
- Reliable(可靠)
JMS provides the facility of assurance that the message will delivered once and only once. You know that duplicate messages create problems. JMS helps you avoiding such problems.(JMS保證消息只會(huì)遞送一次。大家都遇到過(guò)重復(fù)創(chuàng)建消息問(wèn)題,而JMS能幫你避免該問(wèn)題。)
3. JMS的消息模型
JMS具有兩種通信模式:
- Point-to-Point Messaging Domain (點(diǎn)對(duì)點(diǎn))
- Publish/Subscribe Messaging Domain (發(fā)布/訂閱模式)
在JMS API出現(xiàn)之前,大部分產(chǎn)品使用“點(diǎn)對(duì)點(diǎn)”和“發(fā)布/訂閱”中的任一方式來(lái)進(jìn)行消息通訊。JMS定義了這兩種消息發(fā)送模型的規(guī)范,它們相互獨(dú)立。任何JMS的提供者可以實(shí)現(xiàn)其中的一種或兩種模型,這是它們自己的選擇。JMS規(guī)范提供了通用接口保證我們基于JMS API編寫(xiě)的程序適用于任何一種模型。
3.1 Point-to-Point Messaging Domain(點(diǎn)對(duì)點(diǎn)通信模型)
模式圖

涉及到的概念
在點(diǎn)對(duì)點(diǎn)通信模式中,應(yīng)用程序由消息隊(duì)列,發(fā)送方,接收方組成。每個(gè)消息都被發(fā)送到一個(gè)特定的隊(duì)列,接收者從隊(duì)列中獲取消息。隊(duì)列保留著消息,直到他們被消費(fèi)或超時(shí)。
特點(diǎn)
- 每個(gè)消息只要一個(gè)消費(fèi)者
- 發(fā)送者和接收者在時(shí)間上是沒(méi)有時(shí)間的約束,也就是說(shuō)發(fā)送者在發(fā)送完消息之后,不管接收者有沒(méi)有接受消息,都不會(huì)影響發(fā)送方發(fā)送消息到消息隊(duì)列中。
- 發(fā)送方不管是否在發(fā)送消息,接收方都可以從消息隊(duì)列中去到消息(The receiver can fetch message whether it is running or not when the sender sends the message)
- 接收方在接收完消息之后,需要向消息隊(duì)列應(yīng)答成功
3.2 Publish/Subscribe Messaging Domain(發(fā)布/訂閱通信模型)
模式圖

涉及到的概念
在發(fā)布/訂閱消息模型中,發(fā)布者發(fā)布一個(gè)消息,該消息通過(guò)topic傳遞給所有的客戶(hù)端。該模式下,發(fā)布者與訂閱者都是匿名的,即發(fā)布者與訂閱者都不知道對(duì)方是誰(shuí)。并且可以動(dòng)態(tài)的發(fā)布與訂閱Topic。Topic主要用于保存和傳遞消息,且會(huì)一直保存消息直到消息被傳遞給客戶(hù)端。
特點(diǎn)
- 一個(gè)消息可以傳遞個(gè)多個(gè)訂閱者(即:一個(gè)消息可以有多個(gè)接受方)
- 發(fā)布者與訂閱者具有時(shí)間約束,針對(duì)某個(gè)主題(Topic)的訂閱者,它必須創(chuàng)建一個(gè)訂閱者之后,才能消費(fèi)發(fā)布者的消息,而且為了消費(fèi)消息,訂閱者必須保持運(yùn)行的狀態(tài)。
- 為了緩和這樣嚴(yán)格的時(shí)間相關(guān)性,JMS允許訂閱者創(chuàng)建一個(gè)可持久化的訂閱。這樣,即使訂閱者沒(méi)有被激活(運(yùn)行),它也能接收到發(fā)布者的消息。
4. JMS接收消息
在JMS中,消息的產(chǎn)生和消息是異步的。對(duì)于消費(fèi)來(lái)說(shuō),JMS的消息者可以通過(guò)兩種方式來(lái)消費(fèi)消息。
- 同步(Synchronous)
在同步消費(fèi)信息模式模式中,訂閱者/接收方通過(guò)調(diào)用 receive()方法來(lái)接收消息。在receive()方法中,線程會(huì)阻塞直到消息到達(dá)或者到指定時(shí)間后消息仍未到達(dá)。 - 異步(Asynchronous)
使用異步方式接收消息的話,消息訂閱者需注冊(cè)一個(gè)消息監(jiān)聽(tīng)者,類(lèi)似于事件監(jiān)聽(tīng)器,只要消息到達(dá),JMS服務(wù)提供者會(huì)通過(guò)調(diào)用監(jiān)聽(tīng)器的onMessage()遞送消息。
5. JMS編程模型
- 管理對(duì)象(Administered objects)-連接工廠(Connection -
Factories)和目的地(Destination) - 連接對(duì)象(Connections)
- 會(huì)話(Sessions)
- 消息生產(chǎn)者(Message Producers)
- 消息消費(fèi)者(Message Consumers)
- 消息監(jiān)聽(tīng)者(Message Listeners)

1.Connection Factories
創(chuàng)建Connection對(duì)象的工廠,針對(duì)兩種不同的jms消息模型,分別有QueueConnectionFactory和TopicConnectionFactory兩種。可以通過(guò)JNDI來(lái)查找ConnectionFactory對(duì)象??蛻?hù)端使用一個(gè)連接工廠對(duì)象連接到JMS服務(wù)提供者,它創(chuàng)建了JMS服務(wù)提供者和客戶(hù)端之間的連接。JMS客戶(hù)端(如發(fā)送者或接受者)會(huì)在JNDI名字空間中搜索并獲取該連接。使用該連接,客戶(hù)端能夠與目的地通訊,往隊(duì)列或話題發(fā)送/接收消息。
QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) initialCtx.lookup ("primaryQCF");
Queue purchaseQueue = (Queue) initialCtx.lookup ("Purchase_Queue");
Queue returnQueue = (Queue) initialCtx.lookup ("Return_Queue");
2.Destination
目的地指明消息被發(fā)送的目的地以及客戶(hù)端接收消息的來(lái)源。JMS使用兩種目的地,隊(duì)列和話題。如下代碼指定了一個(gè)隊(duì)列和話題:
創(chuàng)建一個(gè)隊(duì)列Session:
QueueSession ses = con.createQueueSession (false, Session.AUTO_ACKNOWLEDGE); //get the Queue object
Queue t = (Queue) ctx.lookup ("myQueue"); //create QueueReceiver
QueueReceiver receiver = ses.createReceiver(t);
創(chuàng)建一個(gè)Topic Session:
QueueSession ses = con.createQueueSession (false, Session.AUTO_ACKNOWLEDGE); //get the Queue object
Queue t = (Queue) ctx.lookup ("myQueue"); //create QueueReceiver
QueueReceiver receiver = ses.createReceiver(t);
3.Connection
Connection表示在客戶(hù)端和JMS系統(tǒng)之間建立的鏈接(對(duì)TCP/IP socket的包裝)。Connection可以產(chǎn)生一個(gè)或多個(gè)Session。跟ConnectionFactory一樣,Connection也有兩種類(lèi)型:QueueConnection和TopicConnection。
連接對(duì)象封裝了與JMS提供者之間的虛擬連接,如果我們有一個(gè)ConnectionFactory對(duì)象,可以使用它來(lái)創(chuàng)建一個(gè)連接。
Connection connection = connectionFactory.createConnection();
4.Session
Session 是我們對(duì)消息進(jìn)行操作的接口,可以通過(guò)session創(chuàng)建生產(chǎn)者、消費(fèi)者、消息等。Session 提供了事務(wù)的功能,如果需要使用session發(fā)送/接收多個(gè)消息時(shí),可以將這些發(fā)送/接收動(dòng)作放到一個(gè)事務(wù)中。
我們可以在連接創(chuàng)建完成之后創(chuàng)建session:
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
這里面提供了參數(shù)兩個(gè)參數(shù),第一個(gè)參數(shù)是是否支持事務(wù),第二個(gè)是事務(wù)的類(lèi)型
5.Producter
消息生產(chǎn)者由Session創(chuàng)建,用于往目的地發(fā)送消息。生產(chǎn)者實(shí)現(xiàn)MessageProducer接口,我們可以為目的地、隊(duì)列或話題創(chuàng)建生產(chǎn)者;
MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);
6.Consumer
消息消費(fèi)者由Session創(chuàng)建,用于接收被發(fā)送到Destination的消息。
MessageConsumer consumer = session.createConsumer(dest);
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer consumer = session.createConsumer(topic);
7.MessageListener
消息監(jiān)聽(tīng)器。如果注冊(cè)了消息監(jiān)聽(tīng)器,一旦消息到達(dá),將自動(dòng)調(diào)用監(jiān)聽(tīng)器的onMessage方法。EJB中的MDB(Message-Driven Bean)就是一種MessageListener。