JMS簡介與ActiveMQ實戰

1. JMS架構

Java消息服務(Java Message Service,簡稱JMS)是用於訪問企業消息系統的開發商中立的API。企業消息系統可以協助應用軟件通過網絡進行消息交互。JMS在其中扮演的角色與JDBC很相似,正如JDBC提供了一套用於訪問各種不同關係數據庫的公共API,JMS也提供了獨立於特定廠商的企業消息系統訪問方式。
使用JMS 的應用程序被稱爲JMS客戶端,處理消息路由與傳遞的消息系統被稱爲JMS Provider,而JMS應用則是由多個JMS 客戶端和一個JMS Provider構成的業務系統。發送消息的JMS客戶端被稱爲生產者(producer),而接收消息的JMS 客戶端則被稱爲消費者(consumer)。同一JMS 客戶端既可以是生產者也可以是消費者。
JMS 的編程過程很簡單,概括爲:應用程序A發送一條消息到消息服務器(也就是JMS Provider)的某個目得地(Destination),然後消息服務器把消息轉發給應用程序B。因爲應用程序A和應用程序B沒有直接的代碼關連,所以兩者實現瞭解偶。如下圖:
這裏寫圖片描述

消息傳遞系統的中心就是消息。一條Message 由三個部分組成:

消息的組成

1. 頭(head)

每條JMS 消息都必須具有消息頭。頭字段包含用於路由和識別消息的值。可以通過多種方式來設置消息頭的值:
a. 由JMS 提供者在生成或傳送消息的過程中自動設置
b. 由生產者客戶機通過在創建消息生產者時指定的設置進行設置
c. 由生產者客戶機逐一對各條消息進行設置

2. 屬性(property)

消息可以包含稱作屬性的可選頭字段。他們是以屬性名和屬性值對的形式制定的。可以將屬性是爲消息頭得擴展,其中可以包括如下信息:創建數據的進程、數據的創建時間以及每條數據的結構。JMS提供者也可以添加影響消息處理的屬性,如是否應壓縮消息或如何在消息生命週期結束時廢棄消息。

3. 主體(body)

包含要發送給接收應用程序的內容。每個消息接口特定於它所支持的內容類型。JMS爲不同類型的內容提供了他們各自的消息類型,但是所有消息都派生自Message接口。
StreamMessage 一種主體中包含Java基元值流的消息。其填充和讀取均按順序進行。
MapMessage 一種主體中包含一組鍵–值對的消息。沒有定義條目順序。
TextMessage 一種主體中包含Java字符串的消息(例如,XML消息)。
ObjectMessage 一種主體中包含序列化Java對象的消息。
BytesMessage 一種主體中包含連續字節流的消息。
例如:MapMessage 消息格式

MapMessage={  
    Header={  
        ... standard headers ...  
        CorrelationID={123-00001}  
    }  
    Properties={  
        AccountID={Integer:1234}  
    }  
    Fields={  
        Name={String:Mark}  
        Age={Integer:47}  
    }   
}  

消息的傳遞模型

JMS支持兩種消息傳遞模型:點對點(point-to-point,簡稱PTP)和發佈/訂閱(publish/subscribe,簡稱pub/sub)。這兩種消息傳遞模型非常相似,但有以下區別:
a. PTP消息傳遞模型規定了一條消息之間能夠傳遞到一個接收方。
b. Pub/sub消息傳遞模型允許一條消息傳遞給多個接收方
每個模型都通過擴展公用基類來實現。例如:javax.jms.QueueJavax.jms.Topic都擴展自javax.jms.Destination類。

1. 點對點消息傳遞

通過點對點的消息傳遞模型,一個應用程序可以向另外一個應用程序發送消息。在此傳遞模型中,目標類型是隊列。消息首先被傳送至隊列目標,然後從改隊列將消息傳送至對此隊列進行監聽的某個消費者,如下圖:
這裏寫圖片描述
一個隊列可以關聯多個隊列發送方和接收方,但一條消息僅傳遞給一個接收方。如果多個接收方正在監聽隊列上的消息,JMS Provider將根據“先來者優先”的原則確定由哪個接收方接受下一條消息。如果沒有接收方在監聽隊列,消息將保留在隊列中,直至接收方連接到隊列爲止。這種消息傳遞模型是傳統意義上的拉模型或輪詢模型。在此列模型中,消息不是自動推動給客戶端的,而是要由客戶端從隊列中請求獲得。

2. 發佈/訂閱消息傳遞

通過發佈/訂閱消息傳遞模型,應用程序能夠將一條消息發送到多個接收方。在此傳送模型中,目標類型是主題。消息首先被傳送至主題目標,然後傳送至所有已訂閱此主題的或送消費者。如下圖:
這裏寫圖片描述

主題目標也支持長期訂閱。長期訂閱表示消費者已註冊了主題目標,但在消息到達目標時該消費者可以處於非活動狀態。當消費者再次處於活動狀態時,將會接收該消息。如果消費者均沒有註冊某個主題目標,該主題只保留註冊了長期訂閱的非活動消費者的消息。與PTP消息傳遞模型不同,pub/sub消息傳遞模型允許多個主題訂閱者接收同一條消息。JMS一直保留消息,直至所有主題訂閱者都接收到消息爲止。pub/sub消息傳遞模型基本上是一個推模型。在該模型中,消息會自動廣播,消費者無須通過主動請求或輪詢主題的方法來獲得新的消息。

上面兩種消息傳遞模型裏,我們都需要定義消息生產者和消費者,生產者把消息發送到JMS Provider的某個目標地址(Destination),消息從該目標地址傳送至消費者。消費者可以同步或異步接收消息,一般而言,異步消息消費者的執行和伸縮性都優於同步消息接收者,體現在:
1. 異步消息接收者創建的網絡流量比較小。單向對東消息,並使之通過管道進入消息監聽器。管道操作支持將多條消息聚合爲一個網絡調用。
2. 異步消息接收者使用線程比較少。異步消息接收者在不活動期間不使用線程。同步消息接收者在接收調用期間內使用線程,結果線程可能會長時間保持空閒,尤其是如果該調用中指定了阻塞超時。
3.對於服務器上運行的應用程序代碼,使用異步消息接收者幾乎總是最佳選擇,尤其是通過消息驅動Bean。使用異步消息接收者可以防止應用程序代碼在服務器上執行阻塞操作。而阻塞操作會是服務器端線程空閒,甚至會導致死鎖。阻塞操作使用所有線程時則發生死鎖。如果沒有空餘的線程可以處理阻塞操作自身解鎖所需的操作,這該操作永遠無法停止阻塞。

2. JMS Provider(ActiveMQ)

特性及優勢

  1. 實現JMS1.1規範,支持J2EE1.4以上。
  2. 可運行與任何JVM和大部分web容器(ActiveMQ works great in any JVM)
  3. 支持多種語言客戶端(java, C, C++, Ajax, ActionScript等等)
  4. 支持多種協議(stomp, openwire, REST)
  5. 良好的Spring支持(ActiveMQ has great Spring Support)
  6. 速度很快,JBossMQ的十倍(ActiveMQ is very fast; often 10x faster than JBossMQ)
  7. 與OpenJMS、JBossMQ等開源jms provider相比,ActiveMQ有apache的支持,持續發展的優勢明顯

Queue與Topic的比較

1. JMS Queue執行load balancer語義

一條消息僅能被一個consumer收到。如果在message發送的時候沒有可用的consumer,那麼它講被保存一直到能處理該message的consumer可用。如果一個consumer收到一條message後卻不響應它,那麼這條消息將被轉到另外一個consumer那兒。一個Queue可以有很多consumer,並且在多個可用的consumer中負載均衡。

2. Topic實現publish和subscribe語義

一條消息被publish時,他將發送給所有感興趣的訂閱者,所以零到多個subscriber將接收到消息的一個拷貝。但是在消息代理接收到消息時,只有激活訂閱的subscriber能夠獲得消息的一個拷貝。

3. 分別對應兩種消息模式

Point-to-Point(點對點),Publisher/Subscriber Model(發佈/訂閱者)
其中在Publicher/Subscriber模式下又有Nondurable subscription(非持久化訂閱)和durable subscription(持久化訂閱)兩種消息處理方式。

Point-to-Point(點對點)消息模式開發流程

1. 生產者(producer)開發流程

1.1 創建 Connection

// 根據url,user和password創建一個jms Connection。  
ActiveMQConnectionFactory connectionFactory   =   new ActiveMQConnectionFactory (user, password, url);  
connection = connectionFactory.createConnection();  
connection.start();  

1.2 創建Session

/**在connection的基礎上創建一個session,同時設置是否支持事務ACKNOWLEDGE標識。 
• AUTO_ACKNOWLEDGE:自動確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收。  
• CLIENT_ACKNOWLEDGE:客戶端確認模式。會話對象依賴於應用程序對被接收的消息調用一個acknowledge()方法。一旦這個方法被調用,會話會確認最後一次確認之後所有接收到的消息。這種模式允許應用程序以一個調用來接收,處理並確認一批消息。注意:在管理控制檯中,如果連接工廠的Acknowledge Policy(確認方針)屬性被設置爲"Previous"(提前),但是你希望爲一個給定的會話確認所有接收到的消息,那麼就用最後一條消息來調用acknowledge()方法。  
• DUPS_OK_ACKNOWLEDGE:允許副本的確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收;而且允許重複確認。在需要考慮資源使用時,這種模式非常有效。注意:如果你的應用程序無法處理重複的消息的話,你應該避免使用這種模式。如果發送消息的初始化嘗試失敗,那麼重複的消息可以被重新發送。 
•  SESSION_TRANSACTED**/  
Session session = connection.createSession(  
transacted, Session.AUTO_ACKNOWLEDGE);  

1.3 創建Destination對象

//需指定其對應的主題(subject)名稱,producer和consumer將根據subject來發送/接收對應的消息  
if (topic) {  
    destination = session.createTopic(subject);  
} else {  
    destination = session.createQueue(subject);  
}  

1.4 創建MessageProducer

//根據Destination創建MessageProducer對象,同時設置其持久模式。   
MessageProducer producer = session.createProducer(destination);  
if (persistent) {  
      producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
} else {  
      producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);  
}  
if (timeToLive != 0) {  
       producer.setTimeToLive(timeToLive);  
}   

1.5 發送消息到隊列(Queue)

//封裝TextMessage消息,使用MessageProducer的send方法將消息發送出去。  
TextMessage message = session.createTextMessage("createMessageText");  
producer.send(message);  

2. 消費者(consumer)開發流程

2.1 實現MessageListener接口

//消費者類必須實現MessageListener接口,然後在onMessage方法中監聽消息到達處理。  

2.2 創建Connection

//根據url,user和password創建一個jms Connection,如果是durable模式,還需要給connection設置一個clientId。  
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);  
Connection connection = connectionFactory.createConnection();  
if (durable && clientId != null && clientId.length() > 0 && !"null".equals(clientId)) {  
      connection.setClientID(clientId);  
}  
connection.setExceptionListener(this);  
connection.start();  

2.3 創建Session和Destination

//與產品類似  

2.4 創建replayProducer【可選】

//可以用來將消息處理結果發送給producer。   
replyProducer = session.createProducer(null);  
replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

2.5 創建MessageConsumer

//根據Destination創建MessageConsumer對象。  
MessageConsumer consumer = null;  
if (durable && topic) {  
     consumer = session.createDurableSubscriber((Topic)destination, consumerName);  
} else {  
     consumer = session.createConsumer(destination);  
}

2.6 消費message

//在onMessage()方法中接收producer發送過來的消息進行處理,並可以通過replyProducer反饋信息給producer   
if (message.getJMSReplyTo() != null) {   
    replyProducer.send(message.getJMSReplyTo(),   
    session.createTextMessage("Reply: " + message.getJMSMessageID()));   
}    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章