Active MQ 高級特性和用法(一):內嵌Active MQ、Active MQ的各種持久化方式、消息(Topic)的持久化訂閱

Active MQ 高級特性和用法(一)內嵌Active MQ、Active MQ的各種持久化方式、消息(Topic)的持久化訂閱

一、內嵌Active MQ

內嵌Active MQ就是在我們的Java程序裏面用代碼生成一個Active MQ的客戶端,雖然我們自己用代碼生成的Active MQ客戶端沒有真正的客戶端的功能多,但是可能還是會用得到的。

    public static void main(String[] args) throws Exception {
        BrokerService brokerService = new BrokerService();
        brokerService.setBrokerName("EmbedMQ");
        brokerService.addConnector("tcp://localhost:62000");
        brokerService.setManagementContext(new ManagementContext());
        brokerService.start();
    }

上述幾行代碼便將一個簡易的Active MQ客戶端內嵌到我們的Java程序中了。我們可以編寫一對生產者-消費者來測試一下,將Active MQ的端口號改爲62000便可以使用我們內置的Active MQ客戶端。

Active MQ消息存儲的持久化

Active MQ持久化方式

    Active MQ的另一個問題就是隻要是軟件就有可能掛掉,掛掉不可怕,怕的是掛掉之後把信息給丟了,怎麼辦,可以進行消息的持久化,Active MQ提供了幾種持久化方式:

  1. AMQ消息存儲-基於文件的存儲方式,它具有寫入速度快和容易恢復的特點。消息存儲在一個個文件中,文件的默認大小爲32M,如果一條消息的大小超過了32M,那麼這個值必須設置大一點。當一個存儲文件中的消息已經全部被消費,那麼這個文件將被標識爲可刪除,在下一個清除階段,這個文件被刪除。AMQ適用於Active MQ5.3之前的版本
  2. KahaDB消息存儲-提供了容量的提升和恢復能力,是現在的默認存儲方式;KahaDB是基於文件的本地數據庫儲存形式,雖然沒有AMQ的速度快,但是它具有強擴展性,恢復的時間比AMQ短,從5.4版本之後KahaDB做爲默認的持久化方式。
  3. JDBC消息存儲-消息基於JDBC存儲的;
  4. Memory消息存儲-基於內存的消息存儲,由於內存不屬於持久化範疇。所以內存存儲不在討論範圍內。

KahaDB

    由於KahaDB是默認的持久化存儲方案。所以即使你不配置任何的KahaDB參數信息,Active MQ也會啓動KahaDB。
    這種情況下,KahaDB文件所在位置是你的Active MQ安裝路徑下的/data/KahaDB子目錄。

關係型數據庫存儲方案

    從ActiveMQ 4+版本開始,ActiveMQ就支持使用關係型數據庫進行持久化存儲——通過JDBC實現的數據庫連接。可以使用的關係型數據庫囊括了目前市面的主流數據庫。

修改Active MQ的配置文件,conf/activemq.xml

1、將將配置文件中的這段配置:

<persistenceAdapter>
	<kahaDB directory="${activemq.base}/data/kahadb"/>
</persistenceAdapter>

修改爲爲:

<persistenceAdapter>
       <jdbcPersistenceAdapter  dataSource="#mysql-ds "/>
</persistenceAdapter>

2、然後在標籤後,增加數據源的配置(與在Spring中的配置一樣):
    打開文件conf/activemq.xml文件後我們可以看到熟悉的beans、bean標籤等,我們知道Active MQ是用Java語言開發的,那我們看到這些標籤是不是可以猜一下Active MQ用沒用Spring進行開發呢,答案是肯定的。我們可以在Active MQ的lib/optional文件夾中找到Spring的幾個核心依賴包,該xml配置文件也導入了Spring的命名空間。

<bean id="mysql-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>

    其中?relaxAutoCommit=true必須有,其他的屬性根據數據庫的配置自行決定。
3、將mysql-connector-java-5.1.34-bin.jar(版本可以自行選擇)放到ActiveMQ的/ lib目錄下。
4、在Mysql數據庫中增加在連接字符串中設置的數據庫名activemq
5、運行後,會發現在庫中增加了3個表
    activemq_acks:用於存儲訂閱關係。如果是持久化Topic,訂閱者和服務器的訂閱關係在這個表保存,主要數據庫字段如下:

  • container:消息的destination
  • sub_dest:如果是使用static集羣,這個字段會有集羣其他系統的信息
  • client_id:每個訂閱者都必須有一個唯一的客戶端id用以區分
  • sub_name:訂閱者名稱
  • selector:選擇器,可以選擇只消費滿足條件的消息。條件可以用自定義屬性實現,可支持多屬性and和or操作
  • last_acked_id:記錄消費過的消息的id

    activemq_lock:在集羣環境中才有用,只有一個Broker可以獲得消息,稱爲Master Broker,其他的只能作爲備份等待Master Broker不可用,纔可能成爲下一個Master Broker。這個表用於記錄哪個Broker是當前的Master Broker。

    activemq_msgs:用於存儲消息,Queue和Topic都存儲在這個表中。主要的數據庫字段如下:

  • id:自增的數據庫主鍵
  • container:消息的destination
  • msgid_prod:消息發送者客戶端的主鍵
  • msg_seq:是發送消息的順序,msgid_prod+msg_seq可以組成jms的messageid
  • expiration:消息的過期時間,存儲的是從1970-01-01到現在的毫秒數
  • msg:消息本體的java序列化對象的二進制數據
  • priority:優先級,從0-9,數值越大優先級越高

消息的持久化訂閱

P2P模式缺省把消息進行持久化,而topic模式是沒有的。

Topic模式下的消息持久化訂閱

正常情況下的topic模式實驗:
  1. 啓動兩個消費者,啓動一個生產者,發送消息,兩個消費者都可以收到。
  2. 關閉一個消費者,生產者發送消息,活躍的消費者可以收到消息,啓動被關閉的消費者,無法收到消息。
  3. 關閉所有消費者,生產者發送消息,在ActiveMQ控制檯可以看見消息已被接收,但沒有被消費,關閉再啓動Active MQ,啓動消費者收不到消息。

如果topic模式下,需要消費者在離線又上線後,不管ActiveMQ是否重啓過,都保證可以接受到消息,就需要進行持久化訂閱

Topic模式消費端代碼
public class JmsDurableTopicConsumer {

    private static final String USERNAME
            = ActiveMQConnection.DEFAULT_USER;//默認連接用戶名
    private static final String PASSWORD
            = ActiveMQConnection.DEFAULT_PASSWORD;//默認連接密碼
    private static final String BROKEURL
            = ActiveMQConnection.DEFAULT_BROKER_URL;//默認連接地址

    public static void main(String[] args) {
        ConnectionFactory connectionFactory;//連接工廠
        Connection connection = null;//連接
        Session session;//會話 接受或者發送消息的線程
        TopicSubscriber messageConsumer;//消費者類型變爲TopicSubscriber
        //實例化連接工廠
        connectionFactory = new ActiveMQConnectionFactory(JmsDurableTopicConsumer.USERNAME,
                JmsDurableTopicConsumer.PASSWORD, JmsDurableTopicConsumer.BROKEURL);

        try {
            //通過連接工廠獲取連接
            connection = connectionFactory.createConnection();
            //需要有個Id
            connection.setClientID("Mark");//設置客戶端ID,需確保ID的唯一性
            //啓動連接
            connection.start();
            //創建session
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            //創建一個消息隊列
            //消息的destination變爲 Topic 
            Topic destination = session.createTopic("DurableTopic2");
            //創建消息消費者-持久訂閱
           // 消費者創建時變爲session.createDurableSubscriber(destination,
           //"任意名字,代表訂閱名 ");
            messageConsumer
                    = session.createDurableSubscriber(destination, "xiangxue");
            messageConsumer.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    try {
                        System.out.println("Accept msg : "
                                + ((TextMessage) message).getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

    Topic模式的發送端是不需要改變的,只需要之前的正常配置就好,因爲發送端是默認將消息持久化的,若想將持久化設置取消掉,可以在發送端加上
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
該行代碼表示,該發送端發送的消息不會被持久化。消息只會保存在內存中。

接下來,我們測試一下,我們啓動了倆個持久訂閱的消費者。
在這裏插入圖片描述

在Active MQ客戶端界面上可以看到:
在這裏插入圖片描述

1、接下來我們發送一條消息;
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
我們可以看到,倆個消費者都已經收到了。

2、我們關掉一個消費者再發一條消息,顯然開着的消費者可以收到了topic消息。
在這裏插入圖片描述

然後,我們打開剛纔關閉的消費者,可以看到它收到了我們剛纔所發的消息。
在這裏插入圖片描述

注意觀察時間戳!

3、然後我們接着實驗,我們將倆個消費者全部關閉,發送一條消息,然後接着關閉我們的Active MQ客戶端。
打開數據庫activemq-msgs表,可以看到表中多了三條數據,這三條數據正式我們剛纔關掉了我們所有的消費者後發送的消息。
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

4、我們重新啓動Active MQ客戶端,並打開剛纔所關閉的倆個消費者,可以看到倆個消費者仍然可以接收到之前所發送的數據(注意看時間戳)。
在這裏插入圖片描述

總結上述結果
  1. 運行生產者,發佈消息,多個消費者可以正常收到。
  2. 關閉一個消費者,運行生產者,發佈消息後再啓動被關閉的消費者,可以收到離線後的消息;
  3. 關閉所有消費者,運行生產者,發佈消息後,關閉ActiveMQ再啓動,啓動所有消費者,都可以收到消息。

注意:生產者端無需另外單獨配置

消息的非持久化

發送端修改
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
    來設置消息本身的持久化屬性爲非持久化。
    重複上述實驗,可以發現,第1,2點保持不變,但是第三點,當關閉ActiveMQ再啓動,消費者關閉後再啓動,是收不到消息的。

說明,即使進行了持久訂閱,但是消息本身如果是不持久化的,ActiveMQ關閉再啓動,這些非持久化的消息會丟失,進行持久訂閱的消費者也是收不到自身離線期間的消息的。

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