消息隊列學習-ActiveMQ(一)

1 前言

1.1 是什麼

MQ = 消息中間件

在這裏插入圖片描述

1.2 消息中間件的特性要求:

  1. api發送和接受
  2. MQ的高可用性
  3. MQ的集羣和容錯配置
  4. MQ的持久化
  5. 延遲發送/定時投遞
  6. 簽收機制
  7. Spring整合

1.3 產品種類

  • Kafka
  • RabbitMQ
  • RocketMQ
  • ActiveMQ
  • 其他

1.4 能幹嘛

存在的問題:

  • 系統之間接口耦合比較嚴重
  • 面對大流量併發時,容易被沖垮
  • 等待同步存在性能問題

要達到的目標:

  1. 解耦
  2. 削峯
  3. 異步

2 安裝步驟

2.1 官網下載

2.2 複製到opt目錄下面

2.3 解壓縮apache-activemq-5.15.9-bin.tar.gz

2.4 在根目錄下mkdir /myacitveMQ

2.5 cp -r apache-activemq-5.15.9 /myacitveMQ

2.6 普通啓動mq: ./activemq start (關閉 stop)
在這裏插入圖片描述
2.7 mq的默認端口是:61616

2.8 三種查看進程的方法 1)ps -ef|grep “activemq”|grep -v grep 2)lsof -i:61616 3)netstat -anp|grep 61616
在這裏插入圖片描述
2.9 帶日誌啓動 ./activemq start > xxx.log

3 啓動控制檯

目標:windows訪問虛擬機上的activemq控制檯

(1)查詢window IP及虛擬機IP

//windows
ipconfig

在這裏插入圖片描述

// linux
ifconfig

在這裏插入圖片描述
(2)測試兩個地址可以相互ping即可
windows:
在這裏插入圖片描述
linux:
在這裏插入圖片描述
(3)關閉window防火牆、linux防火牆
命令如下:

// windows 自己點吧...
// linux
service iptables stop  或 systemctl stop firewalld

(4)訪問activemq控制檯

// 1. 虛擬機linux啓動activemq
./activemq start
// 2. windows瀏覽器訪問 http://192.168.188.128:8161/

在這裏插入圖片描述

注:
(1) 默認的用戶名和密碼是admin和admin
(2)採用61616端口提供JMS服務
(3)採用8161端口提供管理控制檯服務

4 Java編碼實現ActiveMQ通信

4.1 IDEA建maven工程

在這裏插入圖片描述

4.2 pom.xml

		<!--activemq所需jar包-->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-all</artifactId>
			<version>5.15.9</version>
		</dependency>
		<dependency>
			<groupId>org.apache.xbean</groupId>
			<artifactId>xbean-spring</artifactId>
			<version>3.16</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
            <scope>provided</scope>
        </dependency>
		 <dependency>
				 <groupId>junit</groupId>
				 <artifactId>junit</artifactId>
				 <version>4.12</version>
		 </dependency>

4.3 JMS編碼總體架構

在這裏插入圖片描述

4.4 粗說目的地Destination(隊列或主體)

在這裏插入圖片描述

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

點對點消息傳遞域的特點
(1)每個消息只能有一個消費者,類似1對1的關係。
(2)消息的生產者和消費者之間沒有時間上的相關性。無論消費者在生產者發送消息的時候是否處於運行狀態,消費者都可以提取信息。
(3)消息被消費後,隊列中不會再存儲,所以消費者不會消費到已經被消費掉的信息

生產者代碼:

package com.sky.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsProduce {
    private static final String ActiveMQ_URL = "tcp://192.168.188.128: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
        // 兩個參數,第一個叫事務/第二個叫簽收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4. 創建目的地(具體是隊列還是主題)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5. 創建消息的生產者
        MessageProducer messageProducer = session.createProducer(queue);
        // 6. 通過使用messageProducer生產3條消息發送到MQ隊列裏
        for (int i = 1; i <= 3; i++) {
            // 7. 創建消息
            TextMessage textMessage = session.createTextMessage("msg--->" + i);
            // 8. 通過messageProducer發送給mq
            messageProducer.send(textMessage);
        }
        // 9. 關閉資源
        messageProducer.close();
        session.close();
        connection.close();

        System.out.println("***********消息發佈到MQ完成");
    }
}

運行代碼,執行結果如下:
在這裏插入圖片描述
在查看控制檯:
在這裏插入圖片描述
消費者代碼:(Receive())

同步阻塞方式(receive())
訂閱者或接收者調用messageConsumer的receive()方法來接受消息,receive方法能夠在接受消息之前(或超時之前)將一直阻塞。

package com.sky.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    private static final String ActiveMQ_URL = "tcp://192.168.188.128: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
        // 兩個參數,第一個叫事務/第二個叫簽收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4. 創建目的地(具體是隊列還是主題)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.創建消費者
        MessageConsumer messageConsumer = session.createConsumer(queue);
        while (true) {
            TextMessage textMessage = (TextMessage) messageConsumer.receive();
            if (textMessage != null) {
                System.out.println("****消費者收到消息:msg---" + textMessage.getText());
            }else {
                break;
            }
        }
        messageConsumer.close();
        session.close();
        connection.close();

    }
}

運行代碼,執行結果如下:
在這裏插入圖片描述
查看隊列控制檯:
在這裏插入圖片描述
消費者代碼:(監聽方式)

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

package com.sky.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

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

public class JmsConsumer {
    private static final String ActiveMQ_URL = "tcp://192.168.188.128: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
        // 兩個參數,第一個叫事務/第二個叫簽收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4. 創建目的地(具體是隊列還是主題)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.創建消費者
        MessageConsumer messageConsumer = session.createConsumer(queue);


        /*
        while (true) {
            TextMessage textMessage = (TextMessage) messageConsumer.receive();
            if (textMessage != null) {
                System.out.println("****消費者收到消息:msg---" + textMessage.getText());
            }else {
                break;
            }
        }*/

        // 通過監聽的方式來消費消息
        messageConsumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("****消費者收到消息:ListenerMsg---" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        System.in.read();

        messageConsumer.close();
        session.close();
        connection.close();
    }
}

結果同上面類似,不再截圖。

情景小節

  1. 先生產,只啓動1號消費者。問題:1號消費者能消費信息嗎?Y
  2. 先生產,先啓動1號消費者,在啓動2號消費者。
    問題1:1號消費者可以消費嗎?Y
    問題2:2號消費者可以消費嗎?N
  3. 先啓動2個消費者,再生產6個消息,請問,消費情況如何?一人一半

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

發佈訂閱傳遞域的特點
(1)生產者將消息發佈到 topic 中,每個消息可以有多個消費者,屬於1:N的關係。
(2)生產者和消費者之間有時間上的相關性。訂閱某個主題的消費者只能消費自它訂閱之後發佈的消息
(3)生產者生產時,topic 不保存消息,它是無狀態的不落地,假如無人訂閱就去生產,那就是一條廢消息,所以一般先啓動消費者再啓動生產者

JMS規範允許客戶創建持久訂閱,這在一定程度上放鬆了時間上的相關性要求。持久訂閱允許消費者消費它在未處於激活狀態時發送的消息。(類比微信公衆號訂閱

生產者代碼:

package com.sky.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsProduce_Topic {
    private static final String ActiveMQ_URL = "tcp://192.168.188.128:61616";
    private 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
        // 兩個參數,第一個叫事務/第二個叫簽收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4. 創建目的地(具體是隊列還是主題)
        Topic topic = session.createTopic(TOPIC_NAME);
        // 5. 創建消息的生產者
        MessageProducer messageProducer = session.createProducer(topic);
        // 6. 通過使用messageProducer生產3條消息發送到MQ隊列裏
        for (int i = 1; i <= 3; i++) {
            // 7. 創建消息
            TextMessage textMessage = session.createTextMessage("TOPIC_NAME--->" + i);
            // 8. 通過messageProducer發送給mq
            messageProducer.send(textMessage);
        }
        // 9. 關閉資源
        messageProducer.close();
        session.close();
        connection.close();

        System.out.println("***********TOPIC_NAME消息發佈到MQ完成");
    }
}

消費者代碼:

package com.sky.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

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

public class JmsConsumer_Topic {
    private static final String ActiveMQ_URL = "tcp://192.168.188.128:61616";
    private static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是2號消費者");

        // 1. 創建連接工廠,按照給定的URL地址,採用默認用戶名和密碼
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
        // 2. 通過連接工廠,獲得連接connection並啓動
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();

        // 3. 創建會話session
        // 兩個參數,第一個叫事務/第二個叫簽收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4. 創建目的地(具體是隊列還是主題)
        Topic topic = session.createTopic(TOPIC_NAME);
        // 5.創建消費者
        MessageConsumer messageConsumer = session.createConsumer(topic);

        // 通過監聽的方式來消費消息
        messageConsumer.setMessageListener((message) -> {
            if (message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("****消費者收到topic消息:" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
        });

        System.in.read();

        messageConsumer.close();
        session.close();
        connection.close();
    }
}

先執開啓兩個消費者,在生產三條消息,執行結果如下:
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述

4.7 兩大模式的比較

比較項目 Topic模式隊列 Queue模式隊列
工作模式 “訂閱-發佈模式”,如果當前沒有訂閱,消息將會被丟棄,如果有多個訂閱者都會收到消息 “負載均衡”模式,如果當前沒有消費者,消費者也不會丟棄;如果有多個消費者,那麼一條消息也只會發送給其中一個消費者,並且要求消費者ack消息
有無狀態 無狀態 Queue數據默認會在mq服務器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data 下面,也可以配置成DB存儲。
傳遞完整性 如果每日喲訂閱者,消息會被丟棄 消息不會被丟棄
處理效率 由於消息要按照訂閱者的數量進行復制,所以處理性能會隨着訂閱者的增加而明顯降低,並且還要結合不同消息協議自身的性能差異 由於一條消息只能發送給一個消費者,所以就算消費者就算再多,性能也不會有明顯降低。當然不同的消息協議的具體性能也是有差異的

未完待續…

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