在分布式企業級應用程序中,異步消息機制用於有效地協調各個部分的工作。
J2EE為我們提供了JMS和消息驅動豆(Message-Driven Bean),用來實現應用程序各個部件之間的異步消息傳遞。
一.什麼是消息系統?
通常一個消息系統允許分開的未耦合的應用程序之間可靠地異步通信。在企業應用時,需要一種異步的,非阻塞的消息傳遞。比如,一個客戶端可能希望給一個服務器發送一個請求後,不在乎是否馬上能得到回應。這樣,客戶端沒有理由必須等待服務器處理請求。客戶端應用程序在遞交一個請求之後,只需確保請求到達服務器端後,就可以處理其他任務。通常,這是很高效的。消息系統提供了許多其他分布式對象計算模型沒有的優點。它鼓勵在消息產生者和使用者之間的"松耦合",在它們之間有很高程度的事務處理。對於使用者,它不在乎誰產生了消息,產生者是否仍在網絡上以及消息是什麼時候產生的。這就允許建立動態的,可靠的和靈活的系統。整個的子系統能被修改而不會影響系統的其他部分。
另外的優點包括:系統的高度可擴展性,容易與其他系統進行集成,以及高度的可靠性。由於可靠性和可擴展性,使得它們用於解決許多商業和科學計算問題。比如,消息系統是許多應用程序的基礎,這些應用程序可以是工作流,網絡管理,通信服務或供應鏈管理程序。在JAVA技術中,處理異步消息的能力是通過JMS來實現的。JMS最初設計是為了給傳統的消息對象中間件提供一個標准的JAVA接口。而這些產品是在一個企業級應用程序中必須的。現在出現了許多支持JMS的純JAVA的產品。
消息系統類型
通常有兩種消息類型。
1.發布/訂閱(publish/subscribe)
發布/訂閱消息系統支持一個事件驅動模型,消息產生者和使用者都參與消息的傳遞。產生者發布事件,而使用者訂閱感興趣的事件,並使用事件。產生者將消息和一個特定的主題(Topic)連在一起,消息系統根據使用者注冊的興趣,將消息傳給使用者。
2.點對點(Peer to peer)
在點對點的消息系統中,消息分發給一個單獨的使用者。它維持一個"進入"消息隊列。消息應用程序發送消息到一個特定的隊列,而客戶端從一個隊列中得到消息。
二.JMS簡介
JMS的目的是提供給消息系統客戶一個固定的接口,而且與底層的消息提供者無關。這樣,客戶端的應用程序可以在不同的機器和操作系統中移植,而且能在不同的消息系統產品之間轉移。JMS客戶端都是建立在JAVA技術上的,從而也能使用其他JAVAAPI,如JDBC數據庫連接,使用JAVABEAN組件模型,JDNI名字服務,JTA客戶端事務處理控制以及J2SE和J2EE API來實現企業級應用服務程序。
1.JMS對象模型
圖1顯示了JMS對象,用於提供JMS客戶端與JMS服務提供者相連的對象。
ConnectionFactory是一個客戶端用來創建一個Connection的管理對象。由於在Connection創建時有授權和通信建立過程,因此這個對象是比較大的。
Destination對象將一個消息的目的和服務提供者有關的地址及配置信息包裝起來。
Session是JMS實體,用來支持事務處理和異步消息消費。JMS並不需要客戶端的代碼用於異步消息消費或能處理多個並發消息。通常,事務的復雜性都由一個Session來封裝。
一個Session是一個原子單位的工作,與數據庫的事務一樣,要實現多線程事務比較困難。Session提供了在一個線程編程模式下的並發的優點。
MessageProducer和MessageConsumer對象由Session對象創建。用於發送和接受消息。為了確保消息的傳遞,JMS服務提供者處理的消息都要處於PERSISTENT模式。PERSISTENT模式使得JMS提供者出問題後,也能讓消息保存下來。
Session,MessageProducer和MessageConsumer都不支持並發,而ConnectionFactory,Destination和Connection都支持並發。
2.JMS應用程序開發
JMS中的消息
在消息系統中,應用程序之間通信的關鍵是消息。因此使用JMS必須要先理解消息。
在JMS中,消息由三部分組成:
MESSAGE HEADER用於識別消息,比如用於判斷一個給定的消息是否是一個"訂閱者"
PROPERITIES用於與應用程序相關的,提供者相關的和可選項的信息
BODY是消息的內容,支持幾種格式,包括TextMessage(對String一個簡單的封裝)和ObjectMessage(對任意對象的封裝,但必須支持序列化),也支持其他格式。
TextMessage
一個TextMessage是一個String對象的封裝。在只有文本對象傳遞時,是很有用的。它假設許多消息系統是建立在XML上的。從而TextMessage就可以成為包裝它們的容器。
創建一個TextMessage對象很簡單,如下面的代碼:
TextMessage message=session.createMessage();
message.setText("Hello, world!");
ObjectMessage
如名字所示,它是對一個JAVA對象的封裝的消息。任何可序列化的JAVA對象都能用於ObjectMessage,如果必須將多個對象封裝在一個消息裡傳遞,可以使用Collection對象,來包括多個序列化對象。
下面是創建一個ObjectMessage
ObjectMessage message=session.createObjectMessage();
message.setObject(myObject);
創建一個JMS客戶端程序
一個典型的JMS客戶端由下面的幾個基本步驟來創建:
創建一個到消息系統提供者的連接(Connection)
創建一個Session,用於接收和發送消息
創建MessageProducer和MessageConsumer來創建和接收消息
當完成了上述步驟後,一個消息產生者客戶端將創建並發布消息到一個主題,而消息使用者客戶端會接收與一個主題相關的消息。
1.創建一個Connection
一個Connection提供客戶端對底層的消息系統的訪問。並實現資源的分配和管理。通過使用一個ConnectionFactory來創建一個Connection,通常用JDNI來指定:
Connection message=new initialContext();
TopicConnectionFactory topicConnectionFactory=(TopicConnectionFactory);
topic = (Topic) jndiContext.lookup(topicName);
topicConnection =topicConnectionFactory.createTopicConnection();
2.創建一個Session
Session是一個比較大的JMS對象,他提供了生產和消費消息的手段。用於創建消息使用者和消息產生者。
topicSession = topicConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
兩個參數用於控制事務和消息確認。
3.定位一個Topic
用JDNI來定位一個Topic,Topic用於識別發送或接收的消息,在發布/訂閱系統中。訂閱者訂閱一個給定的Topic,而發布者將它發布的消息與一個Topic相連。
下面是創建一個Topic "WeatherReport"
Topic weatherTopic=messaging.lookup("WeatherReport");
4.啟動Connection
在上面的初始化步驟之後,消息流是禁止的,用於防止在初始化時發生不可預料的行為。一旦初始化結束,必須讓Connection啟動消息系統。
topicConnection.start();
5.創建一個消息產生者
在發布/訂閱裡,一個產生者發布消息到一個指定的Topic。下面的代碼顯示創建一個產生者,以及後續的建立和發布一個簡單文本消息。
TopicPublisher publisher=session.createPublisher(weatherTopic);
TexeMessage message=session.createMessage();
message.setText("ssss");
publisher.publish(message);
下面是一個消息使用者的代碼
topicConnection =topicConnectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topicSubscriber = topicSession.createSubscriber(topic);
topicListener = new MsgListener();
topicSubscriber.setMessageListener(topicListener);
topicConnection.start();