JMS 開發步驟、持久化 topic 消息與非持久化 topic 消息

目錄

JMS API 結構與開發步驟

非持久 Topic 消息

持久化 Topic 消息


JMS API 結構與開發步驟

JMS 開發的基本步驟如下:

1、創建一個 JMS Connection Factory

2、通過 Connection Factory 創建 JMS Connection

3、啓動 JMS Connection

4、通過 JMS Connection 創建 JMS Session

5、創建 JMS Destination(目的地)

6、創建 JMS Producer,或者創建 JMS Message,並設置 destination

7、創建 JMS Consumer,或者註冊一個 JMS message listener

8、發送或者接收 JMS message

9、關閉所有的 JMS 資源(connection、session、producer、consumer等)

下面通過具體的代碼進行演示,分別介紹持久化與非持久化的 topic ,環境:Maven 3.6.1 + Java JDK 1.8 + ActiveMQ 5.15.9 + IDEA 2018 ,Mava 管理的 Java SE 應用,它們的 pom.xml 文件都是一樣的,導入 activemq-all 依賴即可:

<?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>

ActiveMQ 簡介 與 Maven 項目基本使用》中已經介紹過消息隊列 queue,即使消費者沒有在運行中,生產者發送的消息,都會在消費者下次啓動時獲取到,所以這裏持久化與非持久化主要針對主題 topic。

非持久 Topic 消息

1、對於非持久化的 Topic 消息的發送:基本與發送隊列一樣,只需要把創建 Destination 的地方,由創建隊列(queue)改成 Topic,例如:Destination destination = session.createTopic("topic-app");

2、對於非持久的 Topic 消息的接收:

1)必須要求接收方在線,然後客戶端再發送消息,接收方纔能接收到消息。

2)同樣把創建 Destination 的地方,由創建 queue 改爲創建 topic。

3)由於不知道客戶端發送了多少 topic 消息,因此可以改爲 while 循環的方式接收。

4)同一個 topic ,某個消費者接收一次後,它不能再消費此消息,其它未消費的客戶端可以繼續接收此消息。

3、非持久化消息生產者將消息發送到中間件後,無論消息有沒有被消費,默認情況下,再次啓動中間件時,消息就沒有了。

生產者代碼如下:

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.util.UUID;
/**
 * 非持久化 topic(主題) 消息生產者
 */
@SuppressWarnings("all")
public class NoPersistenceTopicSender {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            //創建 javax.jms.ConnectionFactory 連接工廠
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            connection.start();//啓動連接,同理還有 stop、close
            //創建 session 會話,設置開啓事務,消息確認模式爲自動確認
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //創建消息主題(topic),主題名稱自己定義。Queue 、Topic 都是 Destination 的字接口
            Destination destination = session.createTopic("topic-app");
            messageProducer = session.createProducer(destination);//根據目的地創建消息生產者
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                TextMessage textMessage = session.createTextMessage("密文" + (i + 1) + ":" + UUID.randomUUID());
                messageProducer.send(textMessage);//生產者發送消息
            }
            session.commit();//批量會話提交。此時消息會被正式發送到中間件
            System.out.println("消息發送完畢...");
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                if (session != null) {
                    session.close();//關閉會話
                }
                if (connection != null) {
                    connection.close();//關閉連接
                }
                if (messageProducer != null) {
                    messageProducer.close();//關閉生產者
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

消費者代碼如下:

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * 非持久化 topic(主題) 消息消費者
 */
@SuppressWarnings("all")
public class NoPersistenceTopicReceiver {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            connection.start();//啓動連接,同理還有 stop、close
            //創建會話 session。開啓事務,消息確認模式爲自動確認
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //創建消息主題(topic),主題名稱與生產者設置的保持一致。Queue 、Topic 都是 Destination 的字接口
            Destination destination = session.createTopic("topic-app");
            messageConsumer = session.createConsumer(destination);//根據目的地創建消息消費者
            Message message = messageConsumer.receive();//receive方法會導致當前線程阻塞,直到接收到消息
            while (message != null) {
                if (message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    System.out.println("收到消息:" + textMessage.getText());
                    session.commit();//確認消息。
                    //接收消息時設置超時時間,單位爲毫秒。如果爲0,則等同於 receive()一致阻塞。
                    //如果超過超時時間,仍然未接收到消息,則返回 null。while 會推出
                    message = messageConsumer.receive(3000);
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                if (session != null) {
                    session.close();//關閉會話
                }
                if (connection != null) {
                    connection.close();//關閉連接
                }
                if (messageConsumer != null) {
                    messageConsumer.close();//關閉消費者
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

啓動 activeMQ 中間件,測試如下:

非持久化 topic 消息時,如果先運行生產者發送消息,再運行消費者接收消息,那麼消費者是接收不到之前發送的消息的,只能接收訂閱主題時間點之後的消息。

持久化 Topic 消息

1、對於持久化 topic 的發送:

    1)在非持久的基礎上設置傳遞模式(JMSDeliveryMode)(缺省時,默認是非持久):

    producer.setDeliveryMode(DeliveryMode.PERSISTENT); //設置所有 message 採用同樣的傳遞模式
    Message.setJMSDeliveryMode(DeliveryMode.PERSISTENT); //爲每個 messge 分別設置傳遞模式

    2)設置完生產者的傳遞模式後,再啓動連接:connection.start();

    3)對於發送的消息,activeMQ 默認情況下,不管消息有沒有被投遞/消費,activeMQ 重啓後,消息就都沒有了。

2、對於持久化 topic 的接收:

1)需要在連接上設置消費者 id,用於標識消費者:connection.setClientID("clientID_100");

2)創建 TopicSubscriber 來訂閱,而不再是 MessageConsumer:session.createDurableSubscriber(destination,"ds_1");

3)設置好主題訂閱後,再啓動連接:connection.start();

4)訂閱者需要先運行一次,表示向消息中間件註冊自己,然後運行客戶端發送消息,此時無論訂閱者是否在線,都能接收到,即使當時訂閱者不在線,下次啓動連接的時候,也會把沒有收到的消息都接收下來。

5)對於註冊成功了的持久化訂閱者,ActiveMQ 默認情況下,重啓之後,仍然保存了註冊了的訂閱者,無需再次註冊。

import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.util.UUID;
/**
 * 持久化 topic(主題) 消息生產者
 */
@SuppressWarnings("all")
public class PersistenceTopicSender {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            //創建 javax.jms.ConnectionFactory 連接工廠
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            //創建 session 會話,設置開啓事務,消息確認模式爲自動確認
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //創建消息主題(topic),主題名稱自己定義。Queue 、Topic 都是 Destination 的子接口
            Destination destination = session.createTopic("topic-app-2");
            messageProducer = session.createProducer(destination);//根據目的地創建消息生產者
            /**設置消息傳遞模式爲持久化,不寫時默認爲非持久化。DeliveryMode.NON_PERSISTENT
             * 設置完生產者的傳遞模式後,再啓動連接:connection.start();
             * */
            messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
            connection.start();//啓動連接,同理還有 stop、close
            int massageTotal = 5;
            for (int i = 0; i < massageTotal; i++) {
                TextMessage textMessage = session.createTextMessage("密碼" + (i + 1) + ":" + UUID.randomUUID());
                messageProducer.send(textMessage);//生產者發送消息
            }
            session.commit();//批量會話提交。此時消息會被正式發送到中間件
            System.out.println("消息發送完畢...");
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                if (session != null) {
                    session.close();//關閉會話
                }
                if (connection != null) {
                    connection.close();//關閉連接
                }
                if (messageProducer != null) {
                    messageProducer.close();//關閉生產者
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * 持久化 topic(主題) 消息消費者
 */
@SuppressWarnings("all")
public class PersistenceTopicReceiver {
    public static void main(String[] args) {
        Connection connection = null;
        Session session = null;
        TopicSubscriber topicSubscriber = null;
        try {
            String brokerURL = "tcp://127.0.0.1:61616";//ActiveMQ 中間件連接地址
            ConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(brokerURL);
            //如果 ActiveMQ 連不上,則拋異常:java.net.ConnectException: Connection refused: connect
            connection = mqConnectionFactory.createConnection();//通過連接工廠獲取連接 javax.jms.Connection
            /**爲持久訂閱設置客戶端id,這樣即使訂閱者不在線,消息中心也能在它下次上線時將消息投遞給它*/
            connection.setClientID("clientID_100");
            //創建會話 session。開啓事務,消息確認模式爲自動確認
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //創建消息主題(topic),主題名稱與生產者設置的保持一致。Queue 、Topic 都是 Destination 的子接口
            Topic topic = session.createTopic("topic-app-2");
            /**createDurableSubscriber(Topic var1, String var2):創建持久訂閱,var1 是訂閱對象,var2 是持久訂閱名稱,自定義即可
             * 主題訂閱接口 TopicSubscriber 繼承了 MessageConsumer 消息消費者接口*/
            topicSubscriber = session.createDurableSubscriber(topic,"ds-1");//根據目的地創建消息消費者
            connection.start();/**設置了主題訂閱後,再啓動連接*/
            System.out.println("訂閱者啓動成功...");
            Message message = topicSubscriber.receive();//receive方法會導致當前線程阻塞,直到接收到消息
            while (message != null) {
                if (message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    System.out.println("收到消息:" + textMessage.getText());
                    session.commit();//確認消息。
                    //接收消息時設置超時時間,單位爲毫秒。如果爲0,則等同於 receive()一致阻塞。
                    //如果超過超時時間,仍然未接收到消息,則返回 null。while 會推出
                    message = topicSubscriber.receive(3000);
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                if (session != null) {
                    session.close();//關閉會話
                }
                if (connection != null) {
                    connection.close();//關閉連接
                }
                if (topicSubscriber != null) {
                    topicSubscriber.close();//關閉消費者
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

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