JMS 基本概念、消息結構、確認模式 acknowledgeMode

目錄

JMS 基本概念

JMS 消息結構

JMS 消息確認模式


JMS 基本概念

提示:可以先參考《ActiveMQ 簡介 與 Maven 項目基本使用 》的編碼,然後更容易理解本文的理論。

1、JMS 全稱  Java Message Service ,Java 消息服務,是 Java EE 中的一個技術。

2、JMS 規範定義了 Java 中訪問消息中間件的接口,並沒有給予實現,實現 JMS 接口的消息中間件成爲 JMS Provider(供應者),例如 ActiveMQ 。

3、JMS Provider:實現 JMS 接口和規範的消息中間件。

4、JMS Message:JMS 的消息,由以下三個部分組成:

1)消息頭:每個消息頭字段都有對應的 getter 和 setter 方法

2)消息屬性:如果需要除消息頭字段以外的值,那麼可以使用消息屬性

3)消息體:封裝具體的消息數據

5、JMS  Producer:消息生產者,創建和發送 JMS 消息的客戶端應用

6、JMS Consumer:消息消費者,接收和處理 JMS 消息的客戶端應用。消息的消費可以採用以下兩種方式之一:

1)同步消費:通過調用消費者的 receive 方法從目的地中顯示的讀取消息數據,receive 方法會一直阻塞到消息到達。

2)異步消費:客戶端可以爲消費者註冊一個消息監聽器,以定義消息到達時所採取的措施/動作。

7、JMS domains:消息傳遞域,JMS 規範中定義了兩種消息轉遞域:點對點(point-to-point,簡寫成 PTP),發佈/訂閱(publish/subscribe,簡寫爲 pub/sub)

8、點對點(point-to-point)消息傳遞域特點如下:

1)每個生產可以對應多個消費者,但是每個消息只能有一個消費者消費

2)消息的生產者和消費者之間沒有時間上的相關性,即無論消費者在生產者發送消息的時候是否已經運行,它啓動後都可以提取消息

9、發佈/訂閱(publish/subscribe)消息傳遞域特點如下:

1)每個消息可以有多個消費者消費

2)生產者和消費者之間有時間上的相關性。訂閱一個主題的消費者只能消費自它訂閱之後生產者發佈的消息。即使消費者訂閱了主題,如果生產者發佈消息的時候,消費者宕機了,那麼它再次啓動的時候,默認也是不能再接收的。但 JMS 規範允許客戶端創建持久訂閱,允許消費者消費它在未激活狀態時發送的消息。

10、在點對點消息傳遞域中,目的地被稱爲隊列(queue),在發佈/訂閱消息傳遞域中,目的地被稱爲主題(topic)

11、Connection Factory:連接工廠,用來創建連接對象,以連接到 JMS 的 Provider(供應商,實現者)

12、JMS Connection:封裝了客戶與 JMS 提供者之間的一個虛擬的連接

13、JMS Session:是生產者和消費者的一個單線程上下文。會話用於創建消息生產者(Producer)、消息消費者(Consumer),和消息(Message)等。會話提供了一個事務性的上下文,一組發送和接收被組合到了一個原子操作中。

Destination 消息發送到的目的地
Acknowledge 簽收
Transaction 事務
JMS Cline 用來收發消息的客戶端應用

JMS 消息結構

1、JMS 消息(Message)由消息頭、消息屬性、消息體組成。

2、消息頭包含消息的識別信息和路由信息,包含一些標準的屬性如下:

標準屬性 描述
JMSDestination JMS 目的地,由 send 方法設置。主要指 queue與topic。自動分配。
JMSDeliveryMode JMS 傳遞模式,由 send 方法設置。分爲持久模式和非持久模式。前者的消息應該被傳送"一次且僅僅一次",即使 JMS 提供者出現故障,該消息也不會丟失,會在服務器恢復後再次傳遞;後者的消息最多會傳送一次,如果JMS Provider 服務器故障,該消息將永久丟失。自動分配。
JMSExpiration JMS 消息過期/到期時間,由 send 方法設置。等於 Destination 的 send 方法中的 timeToLive 值加上發送時刻的 GMT 時間值。如果 timeToLive 等於0,則 JMSExpiration 被設爲0,表示該消息永不過期。如果發送後,在消息過期時間之後還沒有被髮送到目的地,則該消息被清除。自動分配。
JMSPriority JMS 消息優先級,由 send 方法設置。有 0-9 十個級別,0-4是普通消息,5-9是加急消息。JMS 不要求 JMS Provider 嚴格按着十個優先級發送消息,但必須保證加急消息要先於普通消息到達。默認是第4級。自動分配。
JMSMessageID JMS 消息標識 id,由 send 方法設置。唯一識別每個消息的標識,由 JMS Provider 產生。自動分配。
JMSTimestamp JMS 時間戳,由客戶端設置。一個 JMS Provider 在調用 send 方法時自動設置的。它是消息被髮送和消費者實際接收的時間差。自動分配。
JMSCorrelationID JMS 相關性 id,由客戶端設置。用來連接到另外一個消息,典型的應用是在回覆消息中連接到原消息。可以是任意值,不僅僅是 JMSMessageId。
JMSType JMS 消息類型的識別符。由開發者設置。
JMSReplyTo JMS 回覆,由客戶端設置。提供本信息回覆消息的目的地址。
JMSRedelivered JMS 重發,由JMS Provider(供應商)設置

3、消息體:JMS API 定義了 5 種消息體格式,也叫消息類型,可以使用不同的形式發送接收數據,並可以兼容現有的消息格式。包括:TextMessage、MapMessage、BytesMessage、StreamMessage 和 ObjectMessage。它們都是 Message 接口的子類。

4、消息屬性,包含以下三種類型的屬性:

1)應用程序設置和添加的屬性,比如:Message.setStringProperty("username","zhangSan");

2)JMS 定義的屬性:使用 "JMSX" 作爲屬性名的前綴,connection.getMetaData().getJMSXPropertyNames(); 返回連接支持的所有 JMSX 屬性的名字;

3)JMS 供應商特點的屬性

JMS 定義的屬性
JMSXUserID 發送消息的用戶標識,發送時提供商設置
JMSXAppID 發送消息的應用標識,發送時提供商設置
JMSXDeliveryCount 轉發消息重試次數,第一次是1、第二次是2、...,發送時提供商設置
JMSXGroupID 消息所在消息組的標識,由客戶端設置
JMSXGroupSeq 組內消息的序號,第一個消息是1,第二個是2,...,由客戶端設置
JMSXProducerTXID 產生消息的事務的事務標識,發送時提供商設置
JMSXConsumerTXID 消費消息的事務的事務標識,接收時提供商設置
JMSXRcvTimestamp JMS 轉發消息到消費者的時間,接收時提供商設置
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            /**
             * 創建 javax.jms.ConnectionFactory 連接工廠
             * org.apache.activemq.ActiveMQConnectionFactory 中默認設置了大量的參數,還有幾個重載的構造器可以選擇
             */
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            Connection connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            connection.start();//啓動連接,同理還有 stop、close

            //連接元數據
            ConnectionMetaData metaData = connection.getMetaData();
            int jmsMajorVersion = metaData.getJMSMajorVersion();
            int jmsMinorVersion = metaData.getJMSMinorVersion();
            String jmsProviderName = metaData.getJMSProviderName();
            String jmsVersion = metaData.getJMSVersion();
            int providerMajorVersion = metaData.getProviderMajorVersion();
            int providerMinorVersion = metaData.getProviderMinorVersion();
            String providerVersion = metaData.getProviderVersion();
            Enumeration jmsxPropertyNames = metaData.getJMSXPropertyNames();

            System.out.println("jmsMajorVersion = " + jmsMajorVersion);//jmsMajorVersion = 1
            System.out.println("jmsMinorVersion = " + jmsMinorVersion);//jmsMinorVersion = 1
            System.out.println("jmsProviderName = " + jmsProviderName);//jmsProviderName = ActiveMQ
            System.out.println("jmsVersion = " + jmsVersion);//jmsVersion = 1.1
            System.out.println("providerMajorVersion = " + providerMajorVersion);//providerMajorVersion = 5
            System.out.println("providerMinorVersion = " + providerMinorVersion);//providerMinorVersion = 15
            System.out.println("providerVersion = " + providerVersion);//providerVersion = 5.15.9
            while (jmsxPropertyNames.hasMoreElements()){
                Object o = jmsxPropertyNames.nextElement();
                System.out.print(o+"\t");//JMSXUserID	JMSXGroupID	JMSXGroupSeq	JMSXDeliveryCount	JMSXProducerTXID
            }

JMS 消息確認模式

1、JMS 消息只有在被確認之後,才認爲已經成功的消費了。消息的成功消費通常包含三個階段:客戶接收消息、客戶處理消息、消息被確認。消息被確認後,消費者無法再次讀取,相反如果消息未被確認,則消費者可以一直讀取。

2、createSession(boolean transacted, int acknowledgeMode) 

transacted 爲 true 時:表示事務性會話,此時當一個事務被提交時,確認自動發生:session.commit();//確認消息,告訴中間件,消息已經確認接收;與 acknowledgeMode 參數設置無關。

transacted 爲 false 時:表示非事務性會話,此時消息何時被確認取決於創建會話時的應答模式(acknowledgeMode)。與 acknowledgeMode 參數有關。

3、應答/確認模式(acknowledgeMode) 在 transacted 爲 false 時有效,可選值如下:

Session.AUTO_ACKNOWLEDGE 自動確認。當客戶成功的從 receive 方法,或者從 MessageListener.onMessage 方法返回的時候,會話自動確認客戶收到的消息。
Session.CLIENT_ACKNOWLEDGE 客戶確認。客戶通過調用消息(Session)的 acknowledge() 方法確認消息。client 確認模式是在會話層上進行,只要確認一個被消費的消息,將自動確認當前會話中所有已經被消費的消息。比如一個會話中消費者消費了10個消息,然後確認了第8個消息,那麼這10個消息都會被確認。
Session.DUPS_ACKNOWLEDGE 遲鈍確認。該模式只是會話遲鈍的確認消息的提交。如果 JMS Provider 失敗,那麼可能會導致一些重複的消息。如果是重複的消息,那麼 JMS provider 必須把消息頭的 JMSRedelivered 字段設置爲 true。不常用。

4、下面使用環境:Maven 3.6.1 + Java JDK 1.8 + ActiveMQ 5.15.9 + IDEA 2018 稍作演示,Mava 管理的 Java SE 應用。pom.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wmx</groupId>
<artifactId>activeMQ1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-all</artifactId>
        <version>5.15.9</version>
    </dependency>
</dependencies>
</project>

生產者內容如下:

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.util.UUID;
/**
 * 消息發送者
 */
@SuppressWarnings("all")
public class JmsSender {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            /**
             * 創建 javax.jms.ConnectionFactory 連接工廠
             * org.apache.activemq.ActiveMQConnectionFactory 中默認設置了大量的參數,還有幾個重載的構造器可以選擇
             */
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            connection.start();//啓動連接,同理還有 stop、close
            /**
             * Session createSession(boolean transacted, int acknowledgeMode) 創建會話
             * transacted :表示是否開啓事務
             * acknowledgeMode:表示會話確認模式
             */
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            /**
             * createQueue(String queueName):創建消息隊列,指定隊列名稱,消費者可以根據隊列名稱獲取消息
             * Destination 目的地,重點,interface Queue extends Destination
             */
            Destination destination = session.createQueue("queue-app");
            //createProducer(Destination destination):根據目的地創建消息生產者
            MessageProducer producer = session.createProducer(destination);
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                MapMessage mapMessage = session.createMapMessage();//創建一個 Map 消息
                mapMessage.setStringProperty("token", UUID.randomUUID().toString());//設置屬性。口令
                mapMessage.setIntProperty("expires", 60000);////設置屬性。口令失效時間
                mapMessage.setString("key", "嫦娥 " + (i + 1) + " 號");//設置消息內容
                producer.send(mapMessage);//生產者發送消息
                session.commit();//會話提交,發送消息
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                try {
                    session.close();//關閉會話
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();//關閉連接
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

消費者內容如下:

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * 消息消費者
 */
@SuppressWarnings("all")
public class JmsReceiver {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            /**
             * 創建 javax.jms.ConnectionFactory 連接工廠
             * org.apache.activemq.ActiveMQConnectionFactory 中默認設置了大量的參數,還有幾個重載的構造器可以選擇
             */
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            connection.start();//啓動連接,同理還有 stop、close
            /**
             * Session createSession(boolean transacted, int acknowledgeMode) 創建會話
             * transacted :表示是否開啓事務
             * acknowledgeMode:表示會話確認模式
             * 關閉事務,自動確認:表示 receive 方法、MessageListener.onMessage 方法返回後就已經確認了
             */
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            /**
             * createQueue(String queueName):創建消息隊列,指定隊列名稱,消費者可以根據隊列名稱獲取消息
             * Destination 目的地,重點,interface Queue extends Destination
             */
            Destination destination = session.createQueue("queue-app");
            //createProducer(Destination destination):根據目的地創建消息消費者
            MessageConsumer consumer = session.createConsumer(destination);
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                Message message = consumer.receive();//receive方法會導致當前線程阻塞,指導接收到消息
                if (message instanceof MapMessage) {
                    MapMessage mapMessage = (MapMessage) message;//消費者接收消息。因爲對方發送的 Map 消息,所以可以強轉
                    System.out.print((i + 1) + ":" + mapMessage.getStringProperty("token"));//獲取屬性
                    System.out.print(":" + mapMessage.getIntProperty("expires"));//獲取屬性
                    System.out.println(":" + mapMessage.getString("key"));//獲取消息內容
                    mapMessage.acknowledge();//應答/確認消息
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                try {
                    session.close();//關閉會話
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();//關閉連接
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章