ActiveMQ 基礎操作和應用

-----------------------------ActiveMQ

Java消息服務: 兩個系統之間或者分佈式系統之間的信息通信。   一般我們使用 dubbo框架開發之後,因爲有很多個 項目模塊,每一個項目模塊 都是一個 獨立的 java項目,這些java項目之間 如果想進行 【信息】通信,這時候就要使用 【消息中間件服務】,這個【消息中間件服務】就是 我們的 jms規範,我們的 jms規範的 具體實現就是 我們下面要講的 ActiveMQ ,我們是使用 ActiveMQ來 實現 兩個系統或者 分佈式 系統之間的 信息互通的。

 

-----------------到底是啥意思呢?

假如 項目1  想 發送一個 數據(字符串或者對象) 給我們的 項目2 ,我們就必須 讓 【項目1】 先將 需要 發送的 數據 給我們的 【中間件】,然後再由我們的中間件 來替我們給 【項目2】。 兩個獨立的項目,沒有任何耦合關係的兩個項目。可以使用這個中間件來傳遞發送信息。

爲什麼 需要 這樣一個東西呢 ?

 

回想一下之前使用dubbo框架來開發我們的分佈式項目的時候:

(1)生產者 將 服務 註冊 到 我們的【zookeeper註冊中心】。

(2)消費者 訂閱 【zookeeper註冊中心】中的服務 。

(3)這樣子 就 實現了  生產者提供的服務 消費者可以使用。

 

如果 我們的 【消費者】 和 【生產者】 想要進行 【信息通信】,那麼我們就要 加入 一個 【ActiveMQ消息中間件】 ,來讓我們的生產者和消費者兩個獨立 項目 進行信息通信 。

 

 

有哪些優點 ?

發送者A  ------發消息----》 【中介】《------獲取消息----接受者B

(1)我們的發送者A,發送了消息就發送了,不需要等待 接受者接受完消息,發送完就完了,任務就完成了,然後就進行下一次的發送消息的任務。

(2)我們的 發送者A 和  接收者B  是獨立的,互不影響的,沒有任何耦合的。

ActiveMQ 是 JMS 規範的 具體實現 。

 

********需要配置java環境變量的軟件有:tomcat,maven,zookeeper,ActiveMQ。

因爲這些 軟件 都是使用 java語言進行開發的。因爲是java語言開發的,所以解壓後就可以直接使用。

ActiveMQ後臺管理網站:

圓圈1 : 是管理菜單。

圓圈2 : 是管理菜單中最重要的兩個【選項】,左邊的是管理【點對點通信模式】,右邊的是管理【註冊訂閱通信模式】。

在 Linux 中開啓 我們的 ActiveMQ服務:

到這個目錄下,輸入啓動ActiveMQ 和關閉ActiveMQ的命令。

啓動ActiveMQ的命令 :

下面的info是啓動activemq之後的日誌打印。

 

關閉ActiveMQ的命令 :

下面的一些 也是 打印的日誌信息,不是錯誤。

Windows 中 使用我們的 ActiveMQ :

 

  1. 我們在windows中安裝 activeMQ也是解壓之後就可以使用。
  2. 我們在windows中啓動ActiveMQ,先切換到 ActiveMQ的bin目錄下,然後在地址欄(目錄欄)中輸入cmd,然後就可以從控制檯中輸入 activemq start 這個命令來啓動我們的ActiveMQ.
  3. 退出和關閉我們的 ActiveMQ 的常用方法:
    1. 我們最常用的方式就是:ctrl+c  然後 輸入 y,這樣的方式關閉。
    2. 輸入 activemq stop 來關閉。

【生產者】,【消費者】,【中間件服務】的交互模型:

【點對點】

這種通信模式 是: 【基於隊列】 和 【一對一】 的,也就是說,發送者(生產者)發出的一個信息,只能被一個 消費者 【接收到】。 如果 有 很多個 【消費者】註冊到我們的 ActiveMQ 中間件服務器中的時候,也就是有多個【消費者】想要獲取 我們的這個 【生產者】發出的信息的時候,這種情況下也只能有一個 【消費者】獲取到這個信息,因爲是一對一模式,其他的消費者是獲取不到的。

 

【發佈/訂閱】

這種模式 是 【一對多】的,也就是: 有一個【生產者】發送一個信息,然後有【很多個消費者】需要接收我們的這個信息,這時候我們使用這種模式下,我們的activeMQ中間件將這個信息 推送給每一個【消費者】,也就是說 每一個消費者都能獲取到這一個生產者發送過來的信息 。

 

這兩種模式的使用場景

實際開發中,我們一定會遇見【多線程高併發】的情況。當我們涉及到 多線程的情況的時候 我們 選取 【點對點】模式化。那麼這些個線程中,只有一個線程對應的消費者可以獲取到這個信息,因爲我們的【點對點】模式,涉及到了lock的知識,可以看一下我們存放 信息的目錄:

當我們 設置 【ActiveMQ 信息持久化】的時候,我們的ActiveMQ是將接收到的信息 存放到 :/usr/local/apache-activemq-5.15.8/data/kahadb 中的,如下圖:

紅圈就是 我們 【點對點】模式下的 lock線程鎖。 

 

如果我們設置【消息不持久化】,那麼我們的 ActiveMQ中間層服務 會將我們的【消息】,存放在【內存】中

如果我們選取的是【發佈/訂閱】模式:那麼就不用多說了,所有的線程對應的消費者都會獲取到 生產者發送的信息 。

特別需要說明一下的是:

JMS是規範:

因爲JMS是規範,所以JMS API中都是定義的接口,沒有任何的實體類。

ActiveMQ 目前還沒有實現JMS2.0規範,所以不能使用註解是開發,主要原因是:

 我們jdk到8.0了,所以我們jms的規範也就更新到了2.0,jms的註解式開發必須是2.0以上的版本纔可以。但是我們的ActiveMQ目前還沒有實現 JMS2.0規範,所以我們使用ActiveMQ消息中間件進行開發的時候不能使用註解式開發的。

JMS 和 ActiveMQ 所使用到的 相關依賴(jar):

<!-- JMS規範的jar依賴 -->

<dependency>

<groupId>javax.jms</groupId>

<artifactId>javax.jms-api</artifactId>

<version>2.0.1</version> 

</dependency>

 

<!-- activeMQ對jms具體實現的jar依賴 -->

<dependency>

<groupId>org.apache.activemq</groupId>

<artifactId>activemq-client</artifactId>

<version>5.15.7</version>

</dependency>

 

爲什麼JMS的相關依賴是2.0.1呢?

因爲在maven的中央倉庫中,可以找到的 JMS最新最新的依賴就是2.0.1.

如下圖:

 

實際開發小例子:

創建 一個 生產者 和 一個 消費者:

兩個項目中 都要添加 JMS 和ActiveMQ 相關的依賴:

<!--添加 JMS 和 ActiveMQ 相關的依賴 start-->

<!-- JMS規範的jar依賴 -->
<dependency>
    <groupId>javax.jms</groupId>
    <artifactId>javax.jms-api</artifactId>
    <version>2.0.1</version>
</dependency>

<!-- activeMQ對jms具體實現的jar依賴 -->
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-client</artifactId>
    <version>5.15.7</version>
</dependency>

<!--添加 JMS 和 ActiveMQ 相關的依賴 start-->

Idea中:讓 大寫字母小寫/小寫字母大寫 的 快捷鍵。 Ctrl + Shift + U

 

當我們的依賴出現缺少或者衝突的時候,我們應該怎麼解決呢?

當我們添加的依賴中,有缺少自己所需要依賴的jar包,但是沒有添加進去。如:

我們在實現activeMQ的時候需要添加:

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-client</artifactId>
    <version>5.15.7</version>
</dependency>

這一段依賴。

但是activemq-client 這個依賴 有又要依賴 slf4j-api-1.7.25 這個依賴,但是我們在pom文件中添加進去 activeMQ-client這個依賴之後,我們的maven並沒有自動幫我們導入activemq-client依賴所要依賴的slf4j-api-1.7.25,所以在運行的時候就會報出一段紅色的錯誤:

 

 

這一段錯誤就是缺少slf4j-api-1.7.25 這個依賴所導致的,但是程序缺少這個依賴仍然可以正常執行,所以我們處理這種情況,第一種方法是先嚐試 將這個依賴過濾掉:

 過濾的方法如下:

<!-- activeMQ對jms具體實現的jar依賴 -->
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-client</artifactId>
    <version>5.15.7</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

就是使用<exclusions>這個標籤 將我們的 slf4j這個依賴排除掉。 

但是我們這樣做之後,回報出來另外的錯誤:

這個錯誤的意思是:  缺少 slf4j這個jar包,找不到這個jar包。

所以說 這個 jar 我不能直接進行過濾,必須要有的。

 

所以我們 使用第二個方法來解決:

   那就是在pom文件中添加一個 slf4j-api-1.7.25 這個依賴的實現包:

但是我們在pom文件中添加slf4j-api-1.7.25這個jar的依賴的時候我們可以發現,我們mvn倉庫中沒有這個 jar的 依賴,所以我們只能找一個 實現了slf4j這個的實現包,如:添加下面這個依賴包之後就不會報錯了。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
</dependency>

我們不能添加下面這個依賴包:因爲我們添加下面這個依賴之後,我們啓動main方法之後,還是會報之前那個錯,所有我們需要添加上面的那麼依賴包。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>

【點對點的 發送 和 接收   代碼】

生產者 發送信息 代碼如下:

package com.bjpowernode.activemq.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * ClassName:RunMain
 * Package:com.bjpowernode.activemq.queue
 * Decription:
 *
 * @data:2019/2/18 17:10
 * @author:yangZongJian
 */
public class RunMain {

    private static final String BROKER_URL = "tcp://192.168.242.128:61616";

    private static final String DESTINATION_NAME = "myQueue";


    public static void main(String[] args) {
        sendMessage();
    }

    /**
     * 使用【點對點】的模式,發送【信息】。
     */
    public static void sendMessage(){

        ConnectionFactory connectionFactory = null ;
        Connection connection = null ;
        Session session = null ;
        MessageProducer producer = null ;
        Destination destination = null ;
        TextMessage textMessage = null ;

        try {

            //創建一個 【連接工廠】
            connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            //通過 連接工廠 創建一個 【連接通道Connection】 。
            connection = connectionFactory.createConnection();
            //創建換一個會話session
            //第一個參數: 是否開啓事務。Boolean.FALSE:不開啓事務。Boolean.TRUE:開啓事務。
            //第二個參數: 規定 消息確認 機制。Session.AUTO_ACKNOWLEDGE 遇到
            session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
            //有了這個session之後,我們就可以創建 一個【消息】,然後向【消息】中添加:消息的內容,目的地,生產者,消費者。
            //創建一個 文本內容的 消息 。
            textMessage = session.createTextMessage("文本內容的信息......");//創建一個文本內容的 消息。
            //創建 消息的目的地。
            destination = session.createQueue(DESTINATION_NAME);
            //創建一個 生產者
            producer = session.createProducer(destination);
            //讓生產者 發送 信息 。
            producer.send(textMessage); //發消息是沒有返回值的,所以是非阻塞的,發完就完了,不需要等待任何結果的返回。void send()發送消息的方法。

        } catch (JMSException e) {
            e.printStackTrace();
        }finally {
            if(null != producer){
                try {
                    producer.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if(null != session){
                try {
                    session.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if(null != connection){
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

消費者端接收 消息的代碼:

package com.bjpowernode.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * ClassName:RunMain
 * Package:com.bjpowernode.activemq
 * Decription:
 *
 * @data:2019/2/18 19:48
 * @author:yangZongJian
 */
public class RunMain {

    public static final String BROKER_URL = "tcp://192.168.242.128:61616";

    public static final String DESTINATION = "myQueue" ;

    public static void main(String[] args) {
        receiveMessage();
    }


    public static void receiveMessage(){

        ConnectionFactory connectionFactory = null ;
        MessageConsumer consumer = null ;
        Connection connection = null ;
        Session session = null ;
        try {
            //創建一個連接工廠。
            connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            //創建一個連接
            connection = connectionFactory.createConnection();
            /*
                    因爲我們是【點對點】的模式,
                    所以需要我們的消費者自己去 獲取 存放在ActiveMQ中的消息,
                    所以我們獲取到了connection之後需要啓動一下連接,
                    讓這個連接連接到ActiveMQ上,這個步驟之後消費者纔有。
             */
            connection.start();
            //創建一次會話,也就是創建一個session。
            //第一個參數是: 是否開啓事務、
            //第二次參數是:規定 信息確認 規則。
            session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
            //創建一個目的地對象。
            Destination destination = session.createQueue(DESTINATION);
            //有了session,我們就可以創建消費者對象。
            consumer = session.createConsumer(destination);
            Message message = consumer.receive();
            if(message instanceof TextMessage){
                String text = ((TextMessage) message).getText();
                System.out.println(text);
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }finally {
            if(null != consumer){
                try {
                    consumer.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if(null != session){
                try {
                    session.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
            if(null != connection){
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

【消費者】 和 【生產者】 代碼之間的區別,不同之處如下:

  1. 生產者:

創建session之後:

可以使用session創建【消息對象】,【目的地對象】,【生產者對象】 這三個對象。

  1. 消費者:

創建session之後:

可以使用session創建 【目的地對象】,【消費者對象】。

   創建connection連接對象之後:

需要調用start()方法,開啓一下連接。

 

【發佈訂閱 模式 的 發送 和 接收 的代碼】

【發佈訂閱模式】 需要注意的是 : 發佈訂閱模式 是 一種 廣播模式,也就是 我們的activeMQ就是廣播臺,當我們 運行 【生產者】之後,我們的生產者 發出一個消息,這個消息 被我們的 ActiveMQ 接收到 ,接收到之後,因爲我們使用的是 發佈訂閱的模式,所以我們的 activeMQ 按照廣播的模式進行 向 我們的【消費者】 推送和廣播我們的 消息,如果我們的 【消費者 正在等待接收消息過程中】(因爲我們使用的recevie這個方法,所以當我們的消費者沒有獲取到消息的時候,就會阻塞一直等待接收消息),我們的消費者就會接收到 activeMQ 廣播過來的消息,如果我們的 【消費者沒有處於等待】,那麼消費者就錯過了廣播,也就是沒聽到廣播信息,這樣子就沒有獲取到信息。所以說 我們的 【發佈訂閱】的模式會有信息的丟失。 【發佈訂閱模式】 是強行將信息 推送給 消費者,它不管你有沒有處於等待接收的狀態,他只管推送,所以我們在接收消息的時候,需要先啓動我們的消費者,讓消費者處於接收消息等待的狀態,然後我們 再啓動 生產者,讓生產者發出消息,然後 再讓ActiveMQ廣播出這條消息 。

 

【點對點模式】: 我們的點對點模式 就 不會有 信息的丟失,因爲 我們的消費者 在點對點模式中是 自己主動去 自己需要的 信息。 也就是,我們的生產者 產生一條信息之後,這條信息就會被我們ActiveMQ保存起來(持久化),然後只要我們的 服務器一直啓動着,我們的 消費者可以 第一天來取,也可以可以第二天來取,因爲都不會丟失,之所以這樣是因爲,我們的ActiveMQ默認是 將爲取過的信息 持久化的,所以沒有被消費者取過的信息會被 ActiveMQ保存在 磁盤的 data目錄下。

 

【發佈訂閱模式】 是 廣播模式,強行將信息推送給 【消費者】。

【點對點模式】 是 我們 【消費者】主動去 ActiveMQ中,獲取信息。


Topic主題下 生產者代碼 和 消費者代碼:

 就是將 【點對點】 模式下的代碼中的:

 生產者代碼和消費者代碼中:

                      創建目的地對象的代碼修改爲:

                                               CreateTopic()方法就可以了

 

 

【點對點】 和 【發佈/訂閱】的 比較 :

(1)

【點對點】是【一對一】的模式。

【發佈/訂閱】是【一對多】的模式。

(2)

 【點對點】模式 :可以保證每條數據(信息)都可以被接收到

【發佈/訂閱】模式:不能保證每條數據都可以接受到。因爲發佈訂閱給消費者信息的 模式是 廣播模式,只有在聽廣播的消費者纔可以獲取到 信息數據。

  1. 【點對點】: 生產者將 信息 傳遞給我們的 ActiveMQ,然後我們的ActiveMQ會將這個信息數據保存起來,然後等待 我們的消費者自己來獲取,注意了自己來獲取,什麼時候來取都行,都有這條數據,只要我們的ActiveMQ的服務器正常運行。

 【發佈訂閱】:生產者 將信息 傳遞給我們的 ActiveMQ ,然後我們的 ActiveMQ會將這個信息保存起來,然後 也就是 當ActiveMQ 獲取到這個數據信息的時候,我們的ActiveMQ 開始了 廣播模式,也就是 這個時候 那個【消費者】正在監聽我們的ActiveMQ 誰就能獲取到這個數據信息。

  1. 【點對點】:ActiveMQ 中的信息 已經確認被【一個】消費者獲取完畢之後,我們的activemq就會將這個信息刪除掉,當一個消費者獲取到之後就將這個信息刪除,所以其他的消費者獲取不到的。

【發佈訂閱】: ActiveMQ 中的數據信息,被【所有的想要獲取這個信息數據的消費者】獲取後,注意了是所有的想要獲取這個數據信息的消費者都獲取到了之後,我們的ActiveMQ纔會將這個數據信息刪除掉。

 

 

拉模式 與 推模式 :

拉模式 其實就是 點對點模式:

我們 生產者 產生一個 信息數據,然後這個信息數據就保存在我們的ActiveMQ消息中間件中, 這時候 【消費者】來主動獲取這個信息數據,獲取信息完畢確認之後,ActiveMQ 就會將這個信息數據刪除。如果沒有【消費者】主動過來獲取這個信息數據,那麼我們ActiveMQ就會一直將這個 信息數據存放起來,等待消費者的獲取。

推模式 其實就是 發佈訂閱 模式:

我們的 生產者 產生一個 信息數據 ,然後這個信息數據 就保存在我們的ActiveMQ消息中間件中,當我們的ActiveMQ獲取到這個信息數據之後,就開始了廣播模式,向 【想要獲取這個信息數據的所有的消費者】進行廣播,這時候 誰在聽廣播誰就能獲取到我們的這個 信息數據,誰沒有監聽我們的 activeMQ就等於沒有在聽廣播,就獲取不到信息數據。

所以我們想讓我們的消費者獲取到信息數據,我們就必須讓我們的消費者先啓動,等待獲取activceMQ推送過來的廣播消息,然後開啓生產者,讓生產者發出信息,然後通過ActiveMQ廣播信息,強行推送給我們正在監聽的消費者。

當我們 傳遞的信息爲 Object類型 的時候:

我們在傳遞一個Object的信息的時候,如果我們不設置一下可信任的Object類,那麼消費者在啓動的時候會報錯的:會被報出 object這個類是不可信任的。主要異常內容如下:javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: Forbidden class com.bjpowernode.model.User! This class is not trusted to be serialized as ObjectMessage payload. Please take a look at http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes.

我們需要傳遞的這個類應該做哪些處理?

將這個 類 進行 序列化。如果不設置無法在網絡中進行傳輸。

將這個 類 設置爲 可信任的。如果不設置無法被消費者解析獲取。

  1.  connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            List<String> package_url = new ArrayList<String>();
            package_url.add("com.bjpowernode.model");
            //所有可以信任的Object類都可以一起添加進:list集合中。
            connectionFactory.setTrustedPackages(package_url);//這個是手動指定所信任的包。
            connection = connectionFactory.createConnection();
            connection.start();//寫消費者 必須有的,沒有爲什麼。

    上面是第一種添加可以信任Object類的方法。比較麻煩。

     

    第二種 方法是: 給所有的Object類 統一設置爲可信任 。

connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
//給所有的 Object 類 統一設置爲 可以信任。
connectionFactory.setTrustAllPackages(true);
connection = connectionFactory.createConnection();
connection.start();//寫消費者 必須有的,沒有爲什麼。

實際開發中不會 直接接收 一個 Object類型的對象的,我們一般將這個對象轉換成json格式的String字符串來進行接收,也就是最後我們還是將一個Object類型的信息轉換成了String字符串類型的文本信息,然後從後臺接收這個json字符串文本信息。

使用到的技術是fastjson:
String userJsonString = JSONObject.toJSONString(user);

過濾消息的方法:

MapMessage 映射消息:攜帶一組鍵值對的數據作爲有效負載的消息,有效數據值必須是Java原始數據類型(或者它們的包裝類)及String。即:byte, short, int, long, float, double, char, boolean, String。

說白就是 原先的 Map集合,使用鍵值對的方式存放信息。

使用方式如下:

生產者 創建 map映射消息的方法:

MapMessage mapMessage = session.createMapMessage();
mapMessage.setBoolean("booleanKey",true);
mapMessage.setDouble("doubleKey",3.14);
mapMessage.setString("stringKey","張三");

消費者 接收信息後,判斷是否是 map映射消息的方法:

if(message instanceof MapMessage){
    double doubleValue = ((MapMessage) message).getDouble("doubleKey");
    String stringValue= ((MapMessage) message).getString("stringKey");
    System.out.println(doubleValue);
    System.out.println(stringValue);
}

通常來講,使用 instanceof 就是判斷一個實例是否屬於某種類型

 

ActiveMQ事務消息和非事務消息  測試模式【點對點模式】

生產者的session:

  1. 添加事務: 將session的第一個參數設置爲:Boolean.true。這樣子就開啓了事務機制。生產者調用send方法之後,我們立即查看 後臺管理網站,發現消息隊列中沒有增加消息,這是爲什麼呢? 這是因爲 我們在 生產者中開啓事務之後,手動的commit一下才會將消息提交到 ActiveMQ當中。使用 session.commit() 手動 commit。
  2. 沒有添加事務: 我們調用send方法之後就自動將 消息 提交到 我們的 ActiveMQ當中了。

消費者的session:

  1. 添加事務: 如果我們不手動的session.commit()提交一下,雖然我們消費者獲取到了信息,但是 消息獲取沒有確認,所以消息會永久的保存在 ActiveMQ當中了,不會被刪除,因爲消息獲取確認沒有提交。 我們只有調用session.commit()來手動提交一下 消息獲取確認,確認被ActiveMQ接收後就會將這個消息刪除掉。 消息就等於是出隊了。
  2. 不添加事務: 如果不添加事務的話,當我們調動receive方法之後就會根據我們的配置的 消息確認方式 來進行消息確認的提交,消息確認提交的方式的配置就在 session的第二個參數。

需要注意的是: 生產者 和 消費者 可以同時配置 事務,也可以不同時 配置事務機制。

我們可以在完成業務邏輯之後 提交 消息獲取確認,也可以在業務邏輯之前 提交 消息獲取確認,因爲 這個消息獲取確認的 提交 只是爲了讓 ActiveMQ 知道我們消費者已經獲取到了 消息,好讓我們的ActiveMQ 刪除 消息。 我們的 消息確認的提交和業務邏輯的執行時 相互獨立的。

當我們 給 消費者 設置了 事務機制之後,不管第二個參數 採用任何的 確認機制,我們的消息都會在 事務機制的最後 手動session.commit()提交的時候 自動的進行消息確認的提交。就算我們配置第二個參數爲 不提交消息確認,我們在手動commit的時候就已經自動的將 消息確認提交了。

session = connection.createSession(Boolean.true,Session.AUTO_ACKNOWLEDGE);
後面的Session.AUTO_ACKNOWLEDGE第二個參數配置什麼都已經無所謂了,因爲最後 commit的時候都會 自動提交 消息確認。 

但是我們事務機制的標準session寫法:
session = connection.createSession(Boolean.true,Session.SESSION_TRANSACTED);
雖然後面第二個參數寫什麼都無所謂了,但是這樣寫比較規範。

消息的過濾:

生產者所需要的操作 :

TextMessage textMessage = session.createTextMessage(userString);
//實現 消息 的 【過濾篩選】。 給我們的 消息 添加一個 【-標記-】 ,這個標記可以是 int,String,double。
textMessage.setStringProperty("name","張三");
textMessage.setIntProperty("age",18);

實現消息的 【過濾篩選】 的功能,必須在 【生產者】產生【消息】的時候 爲消息添加上標記,這個標記是 我們【消費者】進行篩選過濾操作所需要用的。

消費者所需要的操作:

使用session 創建消費者對象的時候,將【過濾篩選條件】當作參數傳遞給我們消費者對象。到時候我們消費者從 ActiveMQ 中獲取到 消息的時候 就可以根據 這個過濾條件進行篩選了 。

session = connection.createSession(Boolean.TRUE,Session.SESSION_TRANSACTED);
//創建目的地
Destination destination = session.createQueue(DESTINATION);
//創建消費者
consumer = session.createConsumer(destination,"name='張三' and age=18");

這個篩選條件的格式爲: SQL語句中 where 後面的 條件格式 一樣的 。 對比着sql語句中的where 來進行 書寫。

 

 

事務機制:

生產者和消費者都可以添加 事務機制,添加的格式都是一樣的,標準格式如下

session = connection.createSession(Boolean.TRUE,Session.SESSION_TRANSACTED);

在 【生產者】 項目中 添加 事務機制:

在【生產者】中添加了事務機制之後,生產者 發出消息之後,如果不手動session.commit()一下,那麼消息就不會被提交到ActiveMQ當中,所以當生產者在send()完畢消息之後,需要手動session.commit()提交一次。

     在【消費者】 項目中 添加 事務機制:

在【消費者】中添加添加了事務機制之後, 如果在onMessage或者receive方法之後沒有 ,手動的session.commit()一下,雖然我們的 消費者 可以正常的從ActiveMQ 中獲取到消息,但是因爲沒有commit,所以消息的確認沒有提交,所以ActiveMQ中的這個條消息,在被消費者獲取到之後沒有被刪除掉,被持久的保存在服務器磁盤中了。

 

同步接收  和  異步接收

【異步接收】:

(1)異步接收 其實 就是 兩個線程。

(2) 主線程 從上往下執行,遇到

consumer.setMessageListener(new MessageListener() {}

這個方法 就 又分出去 一條線程 ,這個線程 就是 我們的 監聽線程 。

  1. 主線程 從上向下執行完畢之後,不能關閉connection,因爲如果主線程將   connection連接關閉了,我們的 監聽線程就不能正常執行了。
  2. 這是兩個線程 公用一個 連接通道connection。
  3. 主方法執行完了,我們的監聽線程還沒有執行完。監聽線程會一直對ActiveMQ 進行監聽。

異步請求可以實現對ActiveMQ的全天候的監聽。

final Session finalSession = session;
 consumer.setMessageListener(new MessageListener() {
    public void onMessage(Message message) {
        if(message instanceof TextMessage){
            String text = null;
            try {
                text = ((TextMessage) message).getText();
                finalSession.commit();
            } catch (JMSException e) {
                e.printStackTrace();
            }
            System.out.println(text);
        }
    }
});

【同步接收】:

  1. 只有一條線程,主線程,主線程從上往下執行,當 消費者 調用了 recevie()方法之後,調用完成之後 獲取到消息,主線程接着往下執行,執行到最後,沒有可以執行的了,線程銷燬了。

 不能像異步請求那樣自動的全天候的對ActiveMQ進行監聽。

********如果我們 想 讓 同步接收 像 異步接收 那樣 實時的對 ActiveMQ 進行 監聽 ,我們就需要給 我們的 同步接收加一個 死循環:

//創建消費者
consumer = session.createConsumer(destination,"name='張三' and age=18");
//獲取消息,我們使用【異步接受 消息 的方式】

while(true) {
    Message message = consumer.receive();
    if(message instanceof TextMessage){
        String text = ((TextMessage) message).getText();
        System.out.println(text);
    }
    session.commit();
}

(1)Recevie() 方法 其實就是 單線程(主線程)的方法。

(2)Consumer.setMessageListener(new MessageLister(){

 Public void onMessage(Message message){

           業務邏輯代碼;

}

});

 主線程從上往下執行,當遇到 consumer.setMessageListener()這個方法的時候,就會爲我們這個監聽器 分配 一個 單獨的 線程 ,用來監聽 ActiveMQ,我們主線程繼續往下執行,但是我們的 監聽線程繼續監聽ActiveMQ 。

 

特別需要注意的是:   (1)和(2)不能在一起使用,也就是要麼你使用第一種接收請求的方法,也就是使用同步接受方式,要麼你就使用第二個接收請求的方法,也就是異步接收方式,你不能即使用 異步接收 和 同步接收 ,因爲這樣 會 報出 異常的 。 

 

ActiveMQ 消息持久化

ActiveMQ 默認是 進行 消息持久化的,但是 我們的消息 最好不要持久化,因爲數據持久化操作是我們的 redis 和 mysql 的 功能,我們的ActiveMQ的功能就是一箇中介的作用,幫助兩個獨立,互不干涉,不相互耦合的項目  進行 消息通信,起到了轉發消息的作用。

什麼情況下 我們的 消息 會被永久的保存在了 ActiveMQ的data下的kahadb 目錄下了? 

【點對點模式】: 我們的點對點模式 是 【異步提交確認】,也是【拉模式】,所以我們的消費者 是自己 主動去 ActiveMQ 中獲取的消息,如果消費者一直不取,我們的ActiveMQ就會一直幫我們的 消費者存放着,一直到消費者獲取了消息之後,纔會刪除這條消息,所以我們的 當我們 消費者 down機了,那麼消息就永久的存放在了ActiveMQ 。

 【發佈/訂閱模式】:發佈訂閱模式 是 一種 【廣播模式】,也是 【推模式】,所以 消息是 我們的 ActiveMQ 主動強行推送廣播給 所有需要接收的消費者的。 當我生產者 發出這條消息之後,這條消息被ActiveMQ接收到了,然後ActiveMQ會立即進行廣播,在場的所有的消費者,誰正在監聽ActiveMQ,誰就能聽到廣播,誰就能獲取這條信息,而且ActiveMQ 就會廣播一次,不會廣播第二次的。 當我們的消費者接收到了 消息之後,沒有提交信息獲取確認,那麼我們的 ActiveMQ就無法對消息進行刪除,那麼這個信息就永久的存放在了Active的kahadb目錄下了。

信息 持久化 存放的目錄爲:

  1. 我們的 ActiveMQ 默認是將消息持久化的。這樣消息持久化的好處是:當ActiveMQ所在的服務器關閉了,重啓之後, 之前的消息還在ActiveMQ 中,消費者仍然可以進行獲取 。
  2. 如果【消息不持久化】的話,這些 ActiveMQ接收到的消息 都會被保存在 【內存】中,這樣子有一個很大的缺陷,消息很容易丟失,我們的服務器突然停機了,保存在內存中的 消息 全部丟失了。 實現消息不持久化的代碼如下:
producer = session.createProducer(destination);
//設置 producer生產者 傳送的 消息 不持久化。
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
producer.send(textMessage);
//提交一下事務,將我們的 消息 提交到 ActiveMQ 中 。
session.commit();

提交消息之前:

提交消息之後:

當我們將ActiveMQ所在的服務器關閉之後再打開,然後登錄到ActiveMQ管理界面後:

因爲 我們設置的 producer生產者 上傳的消息是不持久化的,所以這些上傳的消息都存放在了 【內存】當中了,所以我們關閉服務器之後,內存被清空,這些消息丟失了,所以出現了 消息減少的情況。

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