什么是JMS
Java消息服務(Java Message Service,簡稱JMS)是用于訪問企業(yè)消息系統(tǒng)的開發(fā)商中立的API。企業(yè)消息系統(tǒng)可以協(xié)助應用軟件通過網(wǎng)絡進行消息交互。JMS在其中扮演的角色與JDBC很相似,正如JDBC提供了一套用于訪問各種不同關系數(shù)據(jù)庫的公共API,JMS也提供了獨立于特定廠商的企業(yè)消息系統(tǒng)訪問方式。
使用JMS的應用程序被稱為JMS客戶端,處理消息路由與傳遞的消息系統(tǒng)被稱為JMS Provider,而JMS應用則是由多個JMS客戶端和一個JMS Provider構成的業(yè)務系統(tǒng)。發(fā)送消息的JMS客戶端被稱為生產(chǎn)者(producer),而接收消息的JMS客戶端則被稱為消費者(consumer)。同一JMS客戶端既可以是生產(chǎn)者也可以是消費者。
JMS的編程過程很簡單,概括為:應用程序A發(fā)送一條消息到消息服務器(也就是JMS Provider)的某個目得地(Destination),然后消息服務器把消息轉發(fā)給應用程序B。因為應用程序A和應用程序B沒有直接的代碼關連,所以兩者實現(xiàn)了解耦。
消息的結構
1. 頭(head)
每條JMS 消息都必須具有消息頭。頭字段包含用于路由和識別消息的值。可以通過多種方式來設置消息頭的值:
a. 由JMS 提供者在生成或傳送消息的過程中自動設置
b. 由生產(chǎn)者客戶機通過在創(chuàng)建消息生產(chǎn)者時指定的設置進行設置
c. 由生產(chǎn)者客戶機逐一對各條消息進行設置
2. 屬性(property)
消息可以包含稱作屬性的可選頭字段。他們是以屬性名和屬性值對的形式制定的。可以將屬性是為消息頭得擴展,其中可以包括如下信息:創(chuàng)建數(shù)據(jù)的進程、數(shù)據(jù)的創(chuàng)建時間以及每條數(shù)據(jù)的結構。JMS提供者也可以添加影響消息處理的屬性,如是否應壓縮消息或如何在消息生命周期結束時廢棄消息。
3. 主體(body)
包含要發(fā)送給接收應用程序的內(nèi)容。每個消息接口特定于它所支持的內(nèi)容類型。JMS為不同類型的內(nèi)容提供了他們各自的消息類型,但是所有消息都派生自Message接口。
StreamMessage 一種主體中包含Java基元值流的消息。其填充和讀取均按順序進行。
MapMessage 一種主體中包含一組鍵--值對的消息。沒有定義條目順序。
TextMessage 一種主體中包含Java字符串的消息(例如,XML消息)。
ObjectMessage 一種主體中包含序列化Java對象的消息。
BytesMessage 一種主體中包含連續(xù)字節(jié)流的消息。
例如:MapMessage 消息格式
MapMessage={
Header={
... standard headers ...
CorrelationID={123-00001}
}
Properties={
AccountID={Integer:1234}
}
Fields={
Name={String:Mark}
Age={Integer:47}
}
}
消息的傳遞模型
JMS支持兩種消息傳遞模型:點對點(point-to-point,簡稱PTP)和發(fā)布/訂閱(publish/subscribe,簡稱pub/sub)。這兩種消息傳遞模型非常相似,但有以下區(qū)別:
a. PTP消息傳遞模型規(guī)定了一條消息之恩能夠傳遞費一個接收方。
b. Pub/sub消息傳遞模型允許一條消息傳遞給多個接收方
每個模型都通過擴展公用基類來實現(xiàn)。例如:javax.jms.Queue和Javax.jms.Topic都擴展自javax.jms.Destination類。
1. 點對點消息傳遞
通過點對點的消息傳遞模型,一個應用程序可以向另外一個應用程序發(fā)送消息。在此傳遞模型中,目標類型時隊列。消息首先被傳送至隊列目標,然后從該隊列將消息傳送至對此隊列進行監(jiān)聽的某個消費者。
一個隊列可以關聯(lián)多個隊列發(fā)送方和接收方,但一條消息僅傳遞給一個接收方。如果多個接收方正在監(jiān)聽隊列上的消息,JMS Provider將根據(jù)“先來者優(yōu)先”的原則確定由哪個消費方接受下一條消息。如果沒有接收方在監(jiān)聽隊列,消息將保留在隊列中,直至接收方連接到隊列為止。這種消息傳遞模型是傳統(tǒng)意義上的拉模型或輪詢模型。在此列模型中,消息不是自動推動給客戶端的,而是要由客戶端從隊列中請求獲得。
2. 發(fā)布/訂閱消息傳遞
通過發(fā)布/訂閱消息傳遞模型,應用程序能夠將一條消息發(fā)送到多個接收方。在此傳送模型中,目標類型是主題。消息首先被傳送至主題目標,然后傳送至所有已訂閱此主題的或送消費者。
主題目標也支持長期訂閱。長期訂閱表示消費者已注冊了主題目標,但在消息到達目標時改消費者可以處于非活動狀態(tài)。當消費者再次處于活動狀態(tài)時,將會接收該消息。如果消費者均沒有注冊某個主題目標,該主題只保留注冊了長期訂閱的非活動消費者的消息。與PTP消息傳遞模型不同,pub/sub消息傳遞模型允許多個主題訂閱者接收同一條消息。JMS一直保留消息,直至所有主題訂閱者都接收到消息為止。pub/sub消息傳遞模型基本上時一個推模型。在該模型中,消息會自動廣播,消費者無須通過主動請求或輪詢主題的方法來獲得新的消息。
上面兩種消息傳遞模型里,我們都需要定義消息生產(chǎn)者和消費者,生產(chǎn)者吧消息發(fā)送到JMS Provider的某個目標地址(Destination),消息從該目標地址傳送至消費者。消費者可以同步或異步接收消息,一般而言,異步消息消費者的執(zhí)行和伸縮性都優(yōu)于同步消息接收者。
使用同步方式接收消息的話,消息訂閱者調(diào)用receive()方法。在receive()中,消息未到達或在到達指定時間之前,方法會阻塞,直到消息可用。
使用異步方式接收消息的話,消息訂閱者需注冊一個消息監(jiān)聽者,類似于事件監(jiān)聽器,只要消息到達,JMS服務提供者會通過調(diào)用監(jiān)聽器的onMessage()遞送消息。
JMS接口
JMS應用程序由如下基本模塊組成:
1.管理對象(Administered objects)-連接工廠(Connection Factories)和目的地(Destination)
2.連接對象(Connections)
3.會話(Sessions)
4.消息生產(chǎn)者(Message Producers)
5.消息消費者(Message Consumers)
6.消息監(jiān)聽者(Message Listeners)
JMS管理對象
管理對象(Administered objects)是預先配置的JMS對象,由系統(tǒng)管理員為使用JMS的客戶端創(chuàng)建,主要有兩個被管理的對象:連接工廠(ConnectionFactory)和目的地(Destination)
這兩個管理對象由JMS系統(tǒng)管理員通過使用Application Server管理控制臺創(chuàng)建,存儲在應用程序服務器的JNDI名字空間或JNDI注冊表。
客戶端使用一個連接工廠對象連接到JMS服務提供者,它創(chuàng)建了JMS服務提供者和客戶端之間的連接。JMS客戶端(如發(fā)送者或接受者)會在JNDI名字空間中搜索并獲取該連接。使用該連接,客戶端能夠與目的地通訊,往隊列或話題發(fā)送/接收消息。讓我們用一個例子來理解如何發(fā)送消息:
QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) initialCtx.lookup ("primaryQCF");
Queue purchaseQueue = (Queue) initialCtx.lookup ("Purchase_Queue");
Queue returnQueue = (Queue) initialCtx.lookup ("Return_Queue");
目的地指明消息被發(fā)送的目的地以及客戶端接收消息的來源。JMS使用兩種目的地,隊列和話題。如下代碼指定了一個隊列和話題。
創(chuàng)建一個隊列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)建一個話題Session
TopicSession ses = con.createTopicSession (false, Session.AUTO_ACKNOWLEDGE); // get the Topic object
Topic t = (Topic) ctx.lookup ("myTopic"); //create TopicSubscriber
TopicSubscriber receiver = ses.createSubscriber(t);
- JMS連接
連接對象封裝了與JMS提供者之間的虛擬連接,如果我們有一個ConnectionFactory對象,可以使用它來創(chuàng)建一個連接。
Connection connection = connectionFactory.createConnection();
創(chuàng)建完連接后,需要在程序使用結束后關閉它:
connection.close();
- JMS 會話(Session)
Session是一個單線程上下文,用于生產(chǎn)和消費消息,可以創(chuàng)建出消息生產(chǎn)者和消息消費者。
Session對象實現(xiàn)了Session接口,在創(chuàng)建完連接后,我們可以使用它創(chuàng)建Session。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- JMS消息生產(chǎn)者
消息生產(chǎn)者由Session創(chuàng)建,用于往目的地發(fā)送消息。生產(chǎn)者實現(xiàn)MessageProducer接口,我們可以為目的地、隊列或話題創(chuàng)建生產(chǎn)者;
MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);
創(chuàng)建完消息生產(chǎn)者后,可以使用send方法發(fā)送消息:
producer.send(message);
- JMS消息消費者
消息消費者由Session創(chuàng)建,用于接受目的地發(fā)送的消息。消費者實現(xiàn)MessageConsumer接口,,我們可以為目的地、隊列或話題創(chuàng)建消費者;
MessageConsumer consumer = session.createConsumer(dest);
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer consumer = session.createConsumer(topic);
- JMS消息監(jiān)聽器
JMS消息監(jiān)聽器是消息的默認事件處理者,他實現(xiàn)了MessageListener接口,該接口包含一個onMessage方法,在該方法中需要定義消息達到后的具體動作。通過調(diào)用setMessageListener方法我們給指定消費者定義了消息監(jiān)聽器
Listener myListener = new Listener();
consumer.setMessageListener(myListener);