“老生”暢談 - ActiveMQ

1、四大 MQ 介紹

       在介紹 ActiveMQ 之前,我想先把當下存在且有一定認可度的四大 MQ 簡單做下說明。爲了便於整體觀看和比對,我列了如下表格:

名稱 單機吞吐量 Topic 數量對吞吐量的影響 時效性 可用性 可靠性 優勢
ActiveMQ 萬級 ms 高,基於主從架構實現高可用性 有較低概率丟失數據
RabbitMQ 萬級 um 高,基於主從架構實現高可用性 有較低概率丟失數據 延遲極低
RocketMQ 十萬級 ms 非常高,分佈式架構 經過參數優化配置,消息可以做到 0 丟失 可支持大量 Topic
Kafka 十萬級 ms 非常高,分佈式架構 經過參數優化配置,消息可以做到 0 丟失 吞吐量

       其中,RabbitMQRocketMQ 是目前中小型公司用的比較多的。RabbitMQ 是用 erlang 語言開發的,RocketMQ 是用的 Java。如果你是做 Java 開發的,最好選用 RocketMQ,可控制性高。
       Kafka 那當然是大數據必選。至於今天要介紹的 ActiveMQ ,用起來比較簡單,但是社區已經不怎麼活躍,疲態日顯。別問我爲啥還在用,我只能告訴你:人在公司,身不由己。

2、JMS

       JMS(Java Messaging Service)是 Java 平臺上有關面向消息中間件的技術規範,就像你可以用 JDBC 去連接 MySql、Oracle 等數據庫一樣,你可以用 JMS 來連接不同廠商的 MQ 產品。

2.1 JMS 的消息格式

  • TextMessage:一個字符串對象
  • MapMessage:一套名稱 - 值對
  • ObjectMessage:一個序列化的 Java 對象
  • BytesMessage:一個字節的數據流
  • StreamMessage:Java 原始值的數據流

2.2 JMS 的消息傳遞類型

  1. P2P(Point - to - Point):
           點對點的,即一個生產者和一個消費者一一對應。一個生成者產生一個消息只能被一個消費者消費,消費完,消息就沒有了。如下圖:
    P2P

  2. P / S(Publish / Subscribe):
           發佈 / 訂閱模式,即一個生產者產生消息並進行發送後,可以由多個消費者進行接收。發佈即接收,沒有消費者就自動結束,不會存在服務器裏。如下圖:
    P2S

3、安裝配置

3.1 安裝

  1. 下載地址:https://activemq.apache.org/components/classic/download/
  2. 按平臺下載:
    download
  3. 下載完後解壓;
  4. 進入 bin 目錄,window 選擇對應的 32 或 64 位文件夾後,直接雙擊“activemq.bat”即可完成啓動,liunx 下執行“./activemq start”完成啓動。然後打開瀏覽器輸入地址:http://localhost:8161/ 進入 index 頁,即表示成功,默認賬號密碼均爲 admin
    index

3.2 修改管理界面密碼

  1. 進入 conf 目錄下;
  2. 打開 jetty-realm.properties 文件;
  3. 找到 admin: admin, admin,格式爲 - 用戶名 : 密碼, [角色名, …]
  4. 想改成什麼密碼修改即可修改即可。

3.3 修改消息傳送密碼

  1. 進入 conf 目錄下;
  2. 打開 credentials.properties 文件;
  3. activemq.username 爲用戶名,activemq.password 爲密碼,按照自己的意願修改;
  4. 打開 activemq.xml 文件,在 上面添加如下配置:
<!-- 添加訪問 ActiveMQ 的賬號密碼 -->
<plugins>
	<simpleAuthenticationPlugin>
		<users>
			<authenticationUser username="${activemq.username}" password="${activemq.password}" groups="users,admins"/>
		</users>
	</simpleAuthenticationPlugin>
</plugins>
  1. 還有一種 jssa 的方式,複雜一點,不過可以精確到消息的讀寫權限控制,感興趣的話,可以搜一搜,這裏就不介紹了。

4、代碼示例

4.1 P2P

Producer:

import javax.jms.*;

public class QueueProducer {
    public static void main(String[] args) throws JMSException {
        // 1. 創建連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
        // 2. 獲取連接
        Connection connection = connectionFactory.createConnection();
        // 3. 啓動連接
        connection.start();
        /* 4. 獲取 session:
         * Session.AUTO_ACKNOWLEDGE = 1,自動確認
         * CLIENT_ACKNOWLEDGE = 2,客戶端手動確認
         * DUPS_OK_ACKNOWLEDGE = 3,自動批量確認
         * SESSION_TRANSACTED = 0,事務提交併確認
         */
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 5. 創建隊列對象
        Queue queue = session.createQueue("test-queue");
        // 6. 創建消息生產者
        MessageProducer producer = session.createProducer(queue);
        // 7. 創建消息
        TextMessage textMessage = session.createTextMessage("Hello, world!");
        // 8. 發送消息
        producer.send(textMessage);
        // 9. 關閉資源
        producer.close();
        session.close();
        connection.close();
    }
}

Consumer:

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

public class QueueConsumer {
    public static void main(String[] args) throws JMSException, IOException {
        // 1. 創建連接工廠
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
        // 2. 獲取連接
        Connection connection = connectionFactory.createConnection();
        // 3. 啓動連接
        connection.start();
        // 4. 獲取 session
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 5. 創建隊列對象
        Queue queue = session.createQueue("test-queue");
        // 6. 創建消息消費者
        MessageConsumer consumer = session.createConsumer(queue);
        // 7. 監聽消息
        consumer.setMessageListener(message -> {
            TextMessage textMessage = (TextMessage) message;
            try {
                System.out.println("接收到消息:" + textMessage.getText());
            } catch (JMSException e) {
                System.out.println("接收消息異常:" + e.getMessage());
            }
        });
        // 8. 等待鍵盤輸入,防止程序退出
        System.in.read();
        // 9. 關閉資源
        consumer.close();
        session.close();
        connection.close();
    }
}

運行示例:

  1. 先運行 Producer,然後在 ActiveMQ 界面會出先一個 Queue:
    Queue
  2. 再運行 Consumer,控制檯會輸出: 接收到消息:Hello, world!,且不會退出;
  3. 多次運行 Producer,觀察 Consumer 會一直輸出。

4.2 P/S

同時創建 2 個消費者,分別修改生產者和消費者創建隊列對象創建主題對象

// 5. 創建主題對象
Topic topic = session.createTopic("test-topic");

運行示例:

  1. 要先運行 2 個 Consumer;
  2. 再運行 Producer;
  3. 觀察兩個 Consumer 控制檯會分別輸出:Topic1:接收到消息:Hello, world!, Topic2:接收到消息:Hello, world!,且不會退出;
  4. 多次運行 Producer,觀察 Consumer 會一直輸出;
  5. ActiveMQ 管理界面如下:
    Topic

5、整合到 SpringBoot

  1. 添加 pom 依賴:
<!-- activemq -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
  1. 添加配置信息:
# activemq 基礎配置
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 生產環境設置密碼
spring.activemq.user=[根據實際情況填寫]
spring.activemq.password=[根據實際情況填寫]
spring.activemq.pool.enabled=false
  1. 編寫生產者工具類:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;

import javax.jms.Destination;

@Component
public class ActivemqUtil {

    private final JmsMessagingTemplate jmsTemplate;

    @Autowired
    public ActivemqUtil(JmsMessagingTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    /**
     * 發送消息
     *
     * @param destination Queue or Topic
     * @param message     消息內容
     */
    public void sendMessage(Destination destination, final String message) {
        jmsTemplate.convertAndSend(destination, message);
    }

}
  1. 添加消費者類:
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;

@Service
public class Consumer {

    @JmsListener(destination = "test-queue")
    public void receiveQueue(String message) {
        System.out.println("receiveQueue: " + message);
    }

}
  1. 修改啓動類:
import com.sohu.usstock.common.utils.ActivemqUtil;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.Resource;
import javax.jms.Destination;

@SpringBootApplication
public class UsstockApplication implements CommandLineRunner {

    @Resource
    private ActivemqUtil activemqUtil;

    public static void main(String[] args) {
        SpringApplication.run(UsstockApplication.class, args);
    }

    @Override
    public void run(String... args) {
        Destination queue = new ActiveMQQueue("test-queue");
        // Destination topic = new ActiveMQTopic("test-topic");
        activemqUtil.sendMessage(queue, "Hello, world!");
    }
    
}
  1. 運行後查看結果,即可。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章