Java編碼實現ActiveMQ通訊


  POM.xml文件

<dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-all</artifactId>
        <version>5.15.11</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.xbean/xbean-spring -->
    <dependency>
        <groupId>org.apache.xbean</groupId>
        <artifactId>xbean-spring</artifactId>
        <version>4.15</version>
    </dependency>

 


  JMS編碼總體架構


    回憶一下以前的JDBC編碼套路

第一步:註冊驅動(僅僅只做一次)
Class.forName("com.mysql.jdbc.com");
第二步:建立連接(Connection)
DriverManager.getConnection(url,user,password);
第三步:創建運行SQL語句(Statement)
connection.createStatement();
第四步:運行語句
rs.executeQuery(sql);
第五步:處理結果集(ResultSet)
第六步:釋放資源
 


  粗說目的地Destination 隊列(Queue)和主題(Topic) 兩大模式特性


   
  在點對點的消息傳遞域中,目的地被稱爲隊列(queue)

點對點消息傳遞域的特點如下:
(1)每個消息只能有一個消費者,類似於1對1的關係。好比個人快遞自己領自己的。
(2)消息的生產者和消費者之間沒有時間上的相關性。無論消費者在生產者發送消息的時候是否處於運行狀態,消費者都可以提取消息。好比我們的發送短信,發送者發送後不見得接收者會即收即看。
(3)消息被消費後隊列中不會再存儲,所以消費者不會消費到已經被消費掉的消息。


    上手案例
      消息生產者
        代碼

package com.demo.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce {
    private static final String ACTIVEMQ_URL = "tcp://192.168.10.130:61616";
    private static final String QUEUE_NAME = "queue01";
    public static void main(String[] args) throws JMSException {
        //1.創建連接工廠,按照給定的URL,採用默認的用戶名密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通過連接工廠,獲得connection並啓動訪問
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.創建會話session
        //兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.創建目的地(具體是隊列queue還是主題topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //5.創建消息的生產者
        MessageProducer messageProducer = session.createProducer(queue);
        //6.通過使用消息生產者,生產三條消息,發送到MQ的隊列裏面
        for (int i = 0; i < 3; i++) {
            //7.創建消息
            TextMessage textMessage = session.createTextMessage("msg---hello" + i);//理解爲一個字符串
            //8.通過messageProducer發送給MQ隊列
            messageProducer.send(textMessage);
        }
        //9.關閉資源
        messageProducer.close();
        session.close();
        System.out.println("****消息發佈到MQ隊列完成");
    }
}

 


        控制檯
      控制檯說明

 Number Of Pending Messages=等待消費的消息,這個是未出隊列的數量,公式=總接收數-總出隊列數。
Number Of Consumers=消費者數量,消費者端的消費者數量。
Messages Enqueued=進隊消息數,進隊列的總消息量,包括出隊列的。這個數只增不減。
Messages Dequeued=出隊消息數,可以理解爲是消費者消費掉的數量。
總結:
當有一個消息進入這個隊列時,等待消費的消息是1,進入隊列的消息是1。
當消息消費後,等待消費的消息是0,進入隊列的消息是1,出隊列的消息是1。
當再來一條消息時,等待消費的消息是1,進入隊列的消息就是2。


      消息消費者
        代碼1-阻塞式消費者

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    private static final String ACTIVEMQ_URL = "tcp://192.168.150.134:61616";
    private static final String QUEUE_NAME = "queue01";
    public static void main(String[] args) throws JMSException {
        //1.創建連接工廠,按照給定的URL,採用默認的用戶名密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通過連接工廠,獲得connection並啓動訪問
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.創建會話session
        //兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.創建目的地(具體是隊列queue還是主題topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //5.創建消息的消費者,指定消費哪一個隊列裏面的消息
        MessageConsumer messageConsumer = session.createConsumer(queue);
        //循環獲取
        while (true) {
            //6.通過消費者調用方法獲取隊列裏面的消息(發送的消息是什麼類型,接收的時候就強轉成什
麼類型)
            //receive(2000l)兩個重載方法,參數是lang類型的時間,等待一段時間後沒有消息就關閉,如果沒有參數則不會關閉

            TextMessage textMessage = (TextMessage) messageConsumer.receive(2000l);
            if (textMessage != null) {
                System.out.println("****消費者接收到的消息:  " + textMessage.getText());
            }else {
                break;
            }
        }
        //7.關閉資源
        messageConsumer.close();
        session.close();
        connection.close();
    }

}

 


        代碼2-異步監聽式消費者

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

public class JmsConsumer2 {
    private static final String ACTIVEMQ_URL = "tcp://192.168.150.134:61616";
    private static final String QUEUE_NAME = "queue01";
    public static void main(String[] args) throws JMSException, IOException {
        //1.創建連接工廠,按照給定的URL,採用默認的用戶名密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通過連接工廠,獲得connection並啓動訪問
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.創建會話session
        //兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.創建目的地(具體是隊列queue還是主題topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //5.創建消息的消費者,指定消費哪一個隊列裏面的消息
        MessageConsumer messageConsumer = session.createConsumer(queue);
        //6.通過監聽的方式消費消息
        /*
        異步非阻塞式方式監聽器(onMessage)
        訂閱者或消費者通過創建的消費者對象,給消費者註冊消息監聽器setMessageListener,
        當消息有消息的時候,系統會自動調用MessageListener類的onMessage方法
        我們只需要在onMessage方法內判斷消息類型即可獲取消息
         */
        messageConsumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (message != null && message instanceof TextMessage) {
                    //7.把message轉換成消息發送前的類型並獲取消息內容
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("****消費者接收到的消息:  " + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println("執行了39行");
        //保證控制檯不關閉,阻止程序關閉,監聽器是異步執行,如果不阻執行System.in.read();可能還沒有監聽就把連接關閉了
        System.in.read();
        //關閉資源
        messageConsumer.close();
        session.close();
        connection.close();
    }

}

 


      編碼小總結
        JMS開發的基本步驟


        兩種消費方式

兩種消費方式
 
同步阻塞方式(receive)
訂閱者或接收者抵用MessageConsumer的receive()方法來接收消息,receive方法在能接收到消息之前(或超時之前)將一直阻塞。

 //循環獲取
        while (true) {
            //6.通過消費者調用方法獲取隊列裏面的消息(發送的消息是什麼類型,接收的時候就強轉成什
麼類型)
            //receive(2000l)兩個重載方法,參數是lang類型的時間,等待一段時間後沒有消息就關閉,如果沒有參數則不會關閉

            TextMessage textMessage = (TextMessage) messageConsumer.receive(2000l);
            if (textMessage != null) {
                System.out.println("****消費者接收到的消息:  " + textMessage.getText());
            }else {
                break;
            }
        }


 
異步非阻塞方式(監聽器onMessage())
訂閱者或接收者通過MessageConsumer的setMessageListener(MessageListener listener)註冊一個消息監聽器,
當消息到達之後,系統會自動調用監聽器MessageListener的onMessage(Message message)方法。


 

 /*
        異步非阻塞式方式監聽器(onMessage)
        訂閱者或消費者通過創建的消費者對象,給消費者註冊消息監聽器setMessageListener,
        當消息有消息的時候,系統會自動調用MessageListener類的onMessage方法
        我們只需要在onMessage方法內判斷消息類型即可獲取消息
         */
        messageConsumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (message != null && message instanceof TextMessage) {
                    //7.把message轉換成消息發送前的類型並獲取消息內容
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("****消費者接收到的消息:  " + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println("執行了39行");
        //保證控制檯不關閉,阻止程序關閉,監聽器是異步執行,如果不阻執行System.in.read();可能還沒有監聽就把連接關閉了
        System.in.read();

如果啓動兩個消費者共同連接一個隊列,當隊列有消息的時候會平均分給兩個消費者,採用類似於負載均衡輪詢的方式


  在發佈訂閱消息傳遞域中,目的地被稱爲主題(topic)

發佈/訂閱消息傳遞域的特點如下:
(1)生產者將消息發佈到topic中,每個消息可以有多個消費者,屬於1:N的關係;
(2)生產者和消費者之間有時間上的相關性。訂閱某一個主題的消費者只能消費自它訂閱之後發佈的消息。
(3)生產者生產時,topic不保存消息它是無狀態的不落地,假如無人訂閱就去生產,那就是一條廢消息,所以,一般先啓動消費者再啓動生產者。
 
JMS規範允許客戶創建持久訂閱,這在一定程度上放鬆了時間上的相關性要求。持久訂閱允許消費者消費它在未處於激活狀態時發送的消息。一句話,好比我們的微信公衆號訂閱
 


    上手案例
      發佈主題生產者
        代碼

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsProducer_Topic {
    public static final String ACTIVEMQ_URL = "tcp://192.168.150.134:61616";
    public static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException {

        //1.創建連接工廠,按照給定的URL,採用默認的用戶名密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通過連接工廠,獲得connection並啓動訪問
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.創建會話session
        //兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.創建目的地(具體是隊列queue還是主題topic)
        Topic topic = session.createTopic(TOPIC_NAME);
        //5.創建消息的生產者
        MessageProducer messageProducer = session.createProducer(topic);
        //6.通過使用消息生產者,生產三條消息,發送到MQ的隊列裏面
        for (int i = 0; i < 3; i++) {
            //7.通過session創建消息
            TextMessage textMessage = session.createTextMessage("TOPIC_NAME---" + i);
            //8.使用指定好目的地的消息生產者發送消息
            messageProducer.send(textMessage);
        }
        //9.關閉資源
        messageProducer.close();
        session.close();
        connection.close();
        System.out.println("****TOPIC_NAME消息發佈到MQ完成");
    }
}

 


        控制檯


      訂閱主題消費者
        代碼

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

public class JmsConsumer_Topic {
    public static final String ACTIVEMQ_URL = "tcp://192.168.150.134:61616";
    public static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是1號消費者");
        //1.創建連接工廠,按照給定的URL,採用默認的用戶名密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通過連接工廠,獲得connection並啓動訪問
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.創建會話session
        //兩個參數transacted=事務,acknowledgeMode=確認模式(簽收)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.創建目的地(具體是隊列queue還是主題topic)
        Topic topic = session.createTopic(TOPIC_NAME);
        //5.創建消息的消費者
        MessageConsumer messageConsumer = session.createConsumer(topic);
        //5.創建消息的消費者,指定消費哪一個隊列裏面的消息
        messageConsumer.setMessageListener(message -> {
            if (message instanceof TextMessage){
                try {
                    String text = ((TextMessage) message).getText();
                    System.out.println(text);
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        System.in.read();
    }
}

 


      先啓動訂閱者再啓動生產者,不然發送的消息是廢消息

  小總結
    兩大模式特效


    兩大模式比較

比較項目       Topic隊列模式     Queue隊列模式
1.工作模式 

Topic隊列模式    "訂閱-發佈"模式,如果當前沒有訂閱者,消息將會被丟棄,如果有多個訂閱者,那麼這些訂閱者都會收到消息    

Queue隊列模式  "負載均衡"模式,如果當前沒有消費者,消息也不會丟棄;如果有多個消費者,那麼一條消息也只會發送給其中一個消費者,並且要求消費者ack信息


2.有無狀態    

Topic隊列模式 無狀態  

Queue隊列模式  Queue數據默認會在mq服務器上已文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面,也可以配置成DB存儲


3.傳遞完整性    

Topic隊列模式 如果沒有訂閱者,消息會被丟棄

Queue隊列模式    消息不會被丟棄


4.處理效率    

 Topic隊列模式 由於消息要按照訂閱者的數量進行復制,所以處理性能會隨着訂閱者的增加而明顯降低,並且還要結合不同消息協議自身的性能差異  

Queue隊列模式  由於一條消息只發送給一個消費者,所以就算消費者再多,性能也不會有明顯降低。當然不同消息協議的具體性能也是有差異的


 

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