ActiveMQ(三)——理解和掌握JMS

一、JMS基本概念

  • JMS是什麼
    JMS Java Message Service,Java消息服務,是JavaEE中的一個技術。
  • JMS規範
    JMS定義了Java中訪問消息中間件的接囗,並沒有給予實現,實現JMS接囗的消息中間件稱爲JMS Provider,例如ActiveMQ
    JMS provider:實現JMS接囗和規範的消息中間件
    JMS message:JMS的消息,JMS消息由以下三部分組成:
    1:消息頭:每個消息頭字段都有相應的getter和setter方法
    2消息屬性:如果需要除消息頭字段以夕卜的值,那麼可以使用消息屬性
    3:消息體:分裝具體的消息數據
  • JMS producer:消息生產者,創建和發送JMS消息的客戶端應用
  • JMS consumer:消息消費者,接收和處理JMS消息的客戶端應用
    消息的消費可以採用以下兩種方法之一
    1:同步消費:通過調用消費者的receive方法從目的地中顯式提取消息,receive方法可以一直阻塞到消息到達。
    2:異步消費:客戶可以爲消費者註冊一個消息監聽器,以定義在消息達到時所採取的動作
  • JMS domains.消息傳遞域,JMS規範中定義了兩種消息傳遞域:點對點(point-to-point,簡寫成PTP)消息傳遞域和發佈/訂閱消息傳遞域(publish/subscrmbe,簡寫成pub/sub)
    1:點對點消息傳遞域的特點如下
    (1)每個消息只能有一個消費者
    (2)消息的生產者和消費者沒有時間上的相關性。無論消費者在生產者發送消
    息的時候是否處於運行狀態,它都可以提取消息。
    ActiveMQ(三)——理解和掌握JMS
    2:發佈/訂閱消傳遞域的特點如下.
    (1)每個消息可以有多個消費者
    (2)生產者和消費者之間有時間上的相關性。訂閱一個主題的消費者只能消費自它訂閱之後發佈的消息。JMS規範允許客戶創建持久訂閱,這在一定程度上放鬆了時間上的相關性要求。持久訂閱允許消費者消費它在未處於激活狀態時發送的消息。
    3:在點對點消息傳遞域中,目的被稱爲隊列(queue);在發佈/訂閱消傳遞域中,
    目的被稱爲主題(topic)
    ActiveMQ(三)——理解和掌握JMS
    Connection factory:連接工廠,用來創建連接對象,以連接到JMS的provider
    JMS Connection:封裝了客戶與JMS提供者之間的一個虛擬的連接
    JMS Session:是生產的和消費消息的個單線程上下文會話用於創建消息生產者(producer)消息消費者(consumer)和消息(message)等。會話提供了一個事務性的上下文,在這個上卜文中,一組發送和接收被組合到了個原子操作中。
  • Destination:消息發送到的目的地
  • Acknowledge:簽收
  • Transaction:事務
  • JMS client:用來收發消息的Java應用
  • Non-JMS client:使到JMS provider本地API寫的應用,用來替換JMS API實現收發消息的功能,通常會提供其他的一些特性,比如:COR、RMIT等。
  • Administered objects:預定義的JMS對象,通常的provider規範中有定義,提供給JMS客戶來司比如:ConnectionFactory和Destination

二、 jms的消息結構

  • JMS消息山以下幾部分組成,消息頭,屬性和消息體
  • 消息頭包含消息的識別信息和路由信息,消息頭包含一些標準的屬性如下
    1.JMS Destination:由send方法設置
    2.JMSDeliveryMode:由send方法設置
    3.JMSExpiration:由send方法設置
    4.JMSPriority:由send方法設置
    5.JMSMessageID:由send方法設置
    6.JMSTimestamp:由客戶端設置
    7.JMSCorre1ationID:由客戶端設置
    8.JMSReplyTo:由客戶端設置
    9.JMSType:由客戶端設置
    10.JMSRedelivered:由JMS Provider設置
    標準的JMS消息頭包含以下屬性:
    1:JMSDestination:消息發送的目的地:主要是指Queue和Topic,自動分配
    2:JMSDe1iveryMode:傳送模式。有兩種:持久模式和非持久模式。一條持久性的消息應該被傳送“一次僅僅一次”,這就意味者如果JMS提供者出現故障,該消息並不會丟失,它會在服務器恢復之後再次傳遞。一條非持久的消息最多會傳送一次,這味這服務器出現故障,該消息將永遠丟失。自動分配
    3:JMSExpiration:消息過期時間,等於Destination的send方法中的timeToLive值加上發送時刻的GMT時間值。如果timeToLive值等於零,則JMSExpiration被設爲零,表示該消息永不過期。如果發送後,在消息過期時間之後消息還沒有被髮送到目的地,則該消息被清除。自動分配
    4:JMSPriority:消息優先級,從0-9十個級別,0到是普通消息,5一9是加急消息。JMS不要求JMS Provider嚴格接照這十個優先級發送消息,但必須保證加尋消息要先於普通消息到達。默認是4級。自動分配
    5:JMSMessageID:唯一識別每個消息的標識,由JMS Provider產生。自動分配
    6:JMSTimestamp:一個JMS Provider在調用send()方法時自動設置的,它是消息被髮送和消費者實際接收的時間差。自動分配
    7:JMSCorre1ationID:用來連接到另外一個消息,典型的應用是在回覆消息中連接到原消息。在大多數情況下,JMSCorre1ationID用於將一條消息標記爲對JMSMessageID標示的上條消息的應答,不過,JMSCorre1ationID可以是任何值,不僅僅是JMSMessageID。由開發者設置
    8:JMSReplyTo:提供本消息回覆消息的目的地。由開發者設置
    9:JMSType:消息類類型的識別符。由開發者設置
    10:JHSRedelivered:如果一個客戶端收到一個設置了JMSRede1ivered屬性的消息,則表示可能客戶端曾經在早些時候收到過該消息,但並沒有簽收(acknowledged),如果該消息被重新傳送,JMSRede1ivered=true反之,
    JMSRedelivered=false。自動設置

  • 消息體,JMS API定義了5種消息體格式,也叫消息類型,可以使用不同形式發送接收數據,並以兼容現有的消息格式。包括:TextMessage、apMessage、BytesMessage、StreamMessage和0bjectMessage
  • 消息屬性,包含以下三種類型的屬性
    1.應用程序設置和添加的屬性,比如:
    Message.setStringProperty(username,username)
    2:JMS定義的屬性
    史用“JMSX”作爲屬性名的前綴,
    connection.getMetaData0,getJMSXPropertyNames(),方法返回所有連接支持
    的JMSX屬性的名字。
    3:JMS供應的特定的屬性
  • JMS定義的屬性如下:
    1:JMSXUserID:發送消息的用戶標識,發送時提供商設置
    2:JMSXAppID:發送消息的應用標識,發送時提供商設置
    3:JMSXDe1iveryCount:轉發消息重試次數,第一次是1,第二次是2,...。發送時提供商沒置
    4:JMSXGroupID:消息所在消息組的標識,由客戶端設置
    5:JMSXGroupSeq::組內消息的序號,第一個小時是1,第二個是2,...。由客戶端設置
    6:JMSXProducerTXID:產生消息的事務的事務標識,發送時提供商設置
    7:JMSXConsumerTXID:消費消息的事務的事務標識,接收時提供商設置
    8:JMSXRcvTimestamp:JMS轉發消息到消費者的時間,接收時提供商設置
    9:JMSXState:假定存在一個消息倉庫,它存儲了每個消息的的單獨拷貝,且這些消息從原始消息被髮送時開始。每個拷貝的狀態有:1(等待),2(準備〕,3(到期)或,4(保留)。由於狀態與生產者和消費者無關,所以它不是由它們來提供。它只和在倉庫中查找消息相關,因此JMS沒有提以這種API。由提供商設置

  • 測試案例
    ActiveMQ(三)——理解和掌握JMS
    ActiveMQ(三)——理解和掌握JMS

三、JMS的可靠性機制

  • 消息接收確認
           JMS消息只有在被確認之後,才認爲已經被成功地消費了。消息的成功消費通常包含三個階段:客戶接收消息、客戶處理消息和消息被確認。
           在事務性會話中,當一個事務被提交的時候,確認自動發生。在非事務性會話中,消息何時被確認取決於創建會話時的應答模式(acknowledgementmode)。該參數有以下三個可選值:
           Session.AUTO_ACKNOWLEDGE:當客戶成功的從receive方法返回的時候,或者從MessageListener.onMessage方法成功返回的時候,會話自動確認客戶收到的消息。
           Session.CLIENT_ACKNOWLEDGE:客戶通過調用消息的acknowledge方法確認消息。需要注意的是,在這種模式中,確認是在會話層上進行,確認一個被消費的消息將自動確認所有已被會話消費的消息。例如,如果一個消息消費者消費了10個消息,然後確認了第5個消息,那麼所有10個消息都被確認。
           Session.DUPS_ACKNOWLEDGE:該選擇只是會話遲鈍的確認消息的提交。如果JMS provider失敗,那麼能會導致一些重複的消息。如果是重複的消息,那麼JMS provider必須把消息頭的JMSRedelivered字段設置爲true。
    ActiveMQ(三)——理解和掌握JMS
  • 消息持久性,JMS支持以下兩種消息提交模式:
    PERSISTENT:指示JMS provider持久保存消息,以保證的消息不會因爲JMS provider的失敗而丟失
    NON_PERSISTENT:不要求JMS provider持久保存消息
  • 消息優先級
    可以使用消息優先級別來指示JMS provider首先提交緊急的消息。優先級分10個級別,從0(最低)到9(最高)。如果不指定優先級,默認級別是4。需要注意的是,JMS provider並不一定保證接照優先級的順序提交消息
  • 消息過期
    可以設置消息在一定時間後過期,默認是永不過期
  • 消息的臨時目的地
    可以通過會話上的createTemporaryQueue方法和createTemporaryTopic
    力法來創建臨時目的地。它們的存在時間只限於創建它們的連接所保持的時間。
    只有創建臨時目的地的連接上的消息消費者才能夠從臨時目的地中提取消息。
    ActiveMQ(三)——理解和掌握JMS
  • 持久訂閱
           首先消息生產者必須使用PERSISTENT提交消息。客戶可以通過會話上的
    createDurab1eSubscriber方法來創建一個持久訂閱,該方法的第一個參數必須
    是一個topic。第二個參數是訂閱的名稱。
           JMS provider會存儲發佈到持久訂閱對應的topic上的消息。如果最初創建
    持久訂閱的客戶或者任何其它客戶,使用相同的連接工廠和連接的客戶ID,相同的主題和相同的到訂閱名,再次調用會話上的createDurab1eSubscriber方法,那麼持久訂閱就會被激活。JMS provider會向客戶發送客戶處於非激活狀態時所發佈的消息。
           持久訂閱在某個時刻只能有一個激活的訂閱者。持久訂閱在創建之後會一直保留,直到應用程序調用會話上的unsubscribe方法。

  • 本地事務
           在一個JMS客戶端,可以使用本地事務來組合消息的發送和接收。JMS Session接口提供了commit和rollback方法。事務提交意味着生產的所有消息被髮送,消費的所有消息被確認;事務回滾意味生產的的所有消息被銷燬,消費的所有消息被恢復並重新提交,除非它們已經過期。
           事務性的會話總是牽涉到事務處理中,commit或rollback方法一旦被調用,一個事務就結束了,而另一個事務被開始。關閉事務性會話將回滾其中的事務。得要注意的是,如果使用請求/回覆機制,即發送一個消息,同時希望在同一個事務中等待接收該消息的回覆,那麼程序將被掛起,因爲知道事務提交,發送操作纔會真正執行。
           需要注意的還有一個,消息的生產和消費不能包含在同一個事務中。

四、JMS的PTP模型

  • JMS PTP(Point-to-Point)模型定義了客戶端如向隊列發送消息,從隊列接收消息,以及瀏覽隊列中的消息
           PTP模型是基於隊列的,生產者發消息到隊列,消費者從隊列接收消息,隊列的存在使得消息的異步傳輸成爲金可能。和郵件系統中的郵箱一樣,隊列可以包含各種消息,JMS Provider提供工具管理隊列的創建、刪除。
  • PTP的一些特點
    1:如果在Session關閉時,有一些消息己經被收到,但還沒有被簽收(acknowledged),那麼,當消費者下次連接到相同的隊列時,這些消息還會被再
    次接收
    2:如果用戶在receive方法中設定了消息選擇條件,那麼不符合條件的消息會留在隊列中,不會被接收到
    3:隊列可以長久地保存消息直到消費者收到消息。消費者不需要因爲擔心消息會丟失而時刻和隊列保持激活的連接狀態,充分體現了異步傳輸模式的優勢

五、JMS的Pub/Sub模型

  • JMSPub/Sub模型定義瞭如何向一個內容節點發布和訂閱消息,這些節點被稱作topic主題,可以被認爲是消息的傳輸中介,發佈者(publisher)發佈消息到主題,訂閱者(subscribe〕從主題訂閱消息。主題使得消息訂閱者和消發佈者保持互相獨立,不需要接觸即可保證消息的傳送。
  • JMSPub/Sub的一些特點.
    1:消息訂閱分爲非持久訂閱和持久訂閱
           非持久訂閱只有當客戶端處於激活狀態,也就是和JMS Provider保持連接狀態才能收到發送到某個主題的消息,而當客戶端處於離線狀態,這個時間段發到主題的消息將會丟失,永遠不會收到。
           持久訂閱時,客戶端向JMS註冊一個識別自己身份的ID,當這個客戶端處於離線時,JMS provider會爲這個ID保存所有發送到主題的消息,當客戶再次連接到JMS provider時,會根據自己的ID得到所有當自己處於離線時發送到主題的消息。
    2:如果用戶在receive方法中設定了消息選擇條件,那麼不符合條件的消息不會被接收:非持久訂閱狀態下,不能恢復或重新派送一個未簽收的消息。只有持久訂閱才能恢復或重新派送一個未簽收的消息。
    4:當所有的消息必須被接收,則用持久訂閱。當丟失消息能夠被容忍,則用非持久訂閱
    六、JMS的API結構(和開發相關)
    ActiveMQ(三)——理解和掌握JMS

七、JMS應用開發的基本步驟
ActiveMQ(三)——理解和掌握JMS

八、非持久的Topic消息示例

  • 對於非持久的Topic消息的發送
    基本跟前面發送隊列信息是一樣的,只是把創建Destination的地方,由創
    建隊列替換成創建Topic,例如:
    Destination destination = session.createTopic(MyTopic");

  • 對於非持久的Topic消息的接收
    1:必須要接收方在線,然後客戶端發送信息,接收方纔能接收到消息
    2:同樣把創建Destination的地方,山創建隊列替換成創建Topic,如:
    Destination destination = session.createTopic('MyTopic);

3:由於不知道客戶端發送多少信息,因此改成while循環的方式了,例如:

Message message = consumer.receive();
while(message!=null){
TextMessage txtMsg= (TextMessage)message;
System.out.println(“收到消息:”+txtMsg.getText());
message = consumer.receive(1000L);
}
  • 完整示例

    public class NoPersistenceSender {
    public static void main(String[] args) throws Exception{
        //連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
    
        //帶事務的session
        Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createTopic("myTopic");
        MessageProducer producer = session.createProducer(destination);
    
        for (int i = 0; i < 3; i++) {
            TextMessage message = session.createTextMessage("message---"+i);
            producer.send(message);
        }
        session.commit();
        session.close();
        connection.close();
    }
    }
public class NoPersistenceReceive {
    public static void main(String[] args) throws Exception{
        //連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();

        //帶事務的session
        final Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);

        Destination destination = session.createTopic("myTopic");
        MessageConsumer consumer = session.createConsumer(destination);

        Message message = consumer.receive();
        while(message!=null){
            TextMessage txtMsg= (TextMessage)message;
            System.out.println("收到消息:"+txtMsg.getText());
            message = consumer.receive(1000L);
        }
        session.commit();
        session.close();
        connection.close();
    }
}

注意:對於非持久性的Topic消息,則需要接收者保持運行狀態,不然消息發送者發出的消息會接收不到。
ActiveMQ(三)——理解和掌握JMS

九、持久的Topic消息示例
1:需要在連接上設置消費者ID,用來識別消費者
2:需要創建TopicSubscriber來訂閱
3:要設置好了過後再start這個connnection
4:消費者一定要先運行一次,等於向消息服務中間件註冊這個消費者,然後再運行客戶端發送消息,這個時候無論消費者是否在線,都會接收到,下次連接的時候,會把沒有收過的消息都接收下來。

public class PersistenceSender {
    public static void main(String[] args) throws Exception{
        //連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection = connectionFactory.createConnection();

        //帶事務的session
        Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createTopic("myTopic");
        MessageProducer producer = session.createProducer(destination);

        producer.setDeliveryMode(DeliveryMode.PERSISTENT);
        connection.start();

        for (int i = 0; i < 3; i++) {
            TextMessage message = session.createTextMessage("message---"+i);
            producer.send(message);
        }
        session.commit();
        session.close();
        connection.close();
    }
}
public class PersistenceReceive {
    public static void main(String[] args) throws Exception{
        //連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection = connectionFactory.createConnection();
        connection.setClientID("cc1");

        //帶事務的session
        final Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);

        Topic destination = session.createTopic("myTopic");
        TopicSubscriber ts = session.createDurableSubscriber(destination,"T1");
        connection.start();

        Message message = ts.receive();
        while(message!=null){
            TextMessage txtMsg= (TextMessage)message;
            System.out.println("收到消息:"+txtMsg.getText());
            message = ts.receive(1000L);
        }
        session.commit();
        session.close();
        connection.close();
    }
}

ActiveMQ(三)——理解和掌握JMS

十、總結

  • 持久化消息
    這是ActiveMQ的默認傳送模式,此模式保證這些消息只被傳送一次和成功使用一次。對於這些消息,可靠性是優先考慮的因素。可靠性的另一個重要方面是確保持久性消息傳送至目標後,消息服務在向消費者傳送它們之前不會丟失這些消息。
    這意味着在持久性消息傳送至目標時,消息服務將其放入持久性數據存儲。如果消息服務由於某種原因導致失敗,它可以恢復此消息並將此消息傳送至相應的消費皆。雖然這樣增加了消息傳送的開銷,但卻增加了可靠性。
  • 非持久化消息
    保證這些消息最多被傳送一次。對於這些消息,可靠性並非主要的考慮因素。此模式並不要求持久性的數據存儲,不保證消息服務由於某種原因導致失敗後消息不會丟失。有兩種方法指定傳送模式:
    1.使用setDe1iveryMode方法,這樣所有的消息都採用此傳送模式;如
    producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT)
    2.使用send方法爲每一條消息設置傳送模式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章