ActiveMQ入門

我不要·~~~404發表於2020-12-29

Apache ActiveMQ是當前最流行的開源的,支援多協議的,基於Java的訊息中介軟體,官網的原話是:Apache ActiveMQ™ is the most popular open source, multi-protocol, Java-based messaging server.

ActiveMQ是一個完全支援JMS1.1和J2EE規範的JMS Provider實現,儘管JMS規範出臺已經是很久的事情了,但是JMS在當今J2EE應用中仍扮演者特殊的地位。

JMS是什麼

JMS全稱Java Message Service,即Java訊息服務應用程式介面,是一個Java平臺中關於面向訊息中介軟體(MOM)的API,用於在兩個應用程式之間,或分散式系統中傳送訊息,進行非同步通訊。Java訊息服務是一個與具體平臺無關的API。

JMS物件模型

img

JMS訊息模型

在JMS標準中,有兩種訊息模型PTP(Point to Point)以及Publish/Subscribe(Pub/Sub)。

PTP,點對點訊息傳送模型

在點對點訊息傳送模型中,傳送者將訊息傳送給一個特殊的訊息佇列,該佇列儲存了所有傳送給它的訊息,消費者從這個佇列中獲取訊息。

PTP的特點:

  • 每個訊息只有一個消費者,即一旦被消費,訊息就不再在訊息佇列中
  • 傳送者和接收者之間在時間上沒有依賴性,也就是說當傳送者傳送了訊息之後,不管接收者有沒有正在執行,都不會影響到訊息被髮送到佇列
  • 接收者在成功接收訊息之後需向佇列傳送確認收到通知

img

Pub/Sub,釋出/訂閱訊息傳遞模型

在釋出/訂閱訊息模型中,釋出者釋出一個訊息,該訊息通過topic傳遞給所有的客戶端。在這種模型中,釋出者和訂閱者彼此不知道對方,是匿名的且可以動態釋出和訂閱topic。在釋出/訂閱訊息模型中,目的地被稱為主題(topic),topic主要用於儲存和傳遞訊息,且會一直儲存訊息直到訊息被傳遞給客戶端。

Pub/Sub特點:

  • 每個訊息可以有多個消費者
  • 釋出者和訂閱者之間有時間上的依賴性。針對某個topic的訂閱者,它必須建立一個或多個訂閱者之後,才能消費釋出者的訊息,而且為了消費訊息,訂閱者必須保持執行的狀態。
  • 為了緩和這樣嚴格的時間相關性,JMS允許訂閱者建立一個可持久化的訂閱,這樣就可以在訂閱者沒有執行的時候也能接收到釋出者的訊息

img

JMS訊息結構

Message主要由三部分組成,分別是訊息頭Header,訊息屬性Properties,以及訊息體Body。

訊息頭中主要內容:

訊息屬性可以理解為訊息的附加訊息頭,屬性名可以自定義。訊息的屬性值可以是String, boolean , byte,short, double, int ,long或float型,Message介面為讀取和寫入屬性提供了若干個取值函式和賦值函式方法。

訊息體的型別:

ActiveMQ的特性

  • 支援多種程式語言
  • 支援多種傳輸協議
  • 有多種持久化方式

ActiveMQ的安裝

安裝環境:JDK1.8,CentOS7
下載地址:http://activemq.apache.org/components/classic/download/
CentOS在連網的情況下也可以通過wget(如果wget命令不存在可以通過yum install wget進行安裝)命令獲取軟體包,如:wget https://archive.apache.org/dist/activemq/5.15.10/apache-activemq-5.15.10-bin.tar.gz

提取檔案: tar -zxvf apache-activemq-5.15.10-bin.tar.gz -C /vartar -zxvf apache-activemq-5.15.10-bin.tar.gz -C /var

重新命名:mv /var/apache-activemq-5.15.10/ /var/activemq/

ActiveMQ解壓後的目錄結構:

img

在/etc/profile檔案中新增Java環境變數:

export JAVA_HOME=/var/jdk1.8.0
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

ActiveMQ解壓後就可以使用,bin目錄下可執行activemq可以進行ActiveMQ的啟動停止。

img

ActiveMQ服務

前面使用命令執行ActiveMQ,但最好的方式是將ActiveMQ作為服務啟動,使用system服務可以保證ActiveMQ在系統啟動時自動啟動。

建立ActiveMQ服務步驟:

  1. 建立一個systemd服務檔案:
vi /usr/lib/systemd/system/activemq.service
  1. 在服務檔案中新增以下內容
[Unit]
Description=ActiveMQ service
After=network.target

[Service]
Type=forking
ExecStart=/var/activemq/bin/activemq start
ExecStop=/var/activemq/bin/activemq stop
User=root
Group=root
Restart=always
RestartSec=9
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=activemq

[Install]
WantedBy=multi-user.target
  1. 產看Java安裝目錄:whereis java
  2. 設定activemq配置檔案/var/activemq/bin/env中的JAVA_HOME
# Location of the java installation
# Specify the location of your java installation using JAVA_HOME, or specify the
# path to the "java" binary using JAVACMD
# (set JAVACMD to "auto" for automatic detection)
JAVA_HOME="/var/jdk1.8.0"
JAVACMD="auto"
  1. 通過systemctl管理activemq啟停
  • 啟動activemq服務:systemctl start activemq
  • 檢視服務狀態:systemctl status activemq
  • 建立軟體連結:ln -s /usr/lib/systemd/system/activemq.service /etc/systemd/system/multi-user.target.wants/activemq.service
  • 開機自啟:systemctl enable activemq
  • 檢測是否開啟成功:systemctl list-unit-files |grep activemq
  • 停止activemq服務:systemctl stop activemq

img

ActiveMQ的Web管理平臺

ActiveMQ自帶有Web管理平臺,預設使用8161埠,服務啟動後在瀏覽器輸入http://服務IP:8161/admin 即可進入,預設配置的賬戶admin,密碼也是admin。

img

如果服務啟動後頁面無法訪問可能是防火牆內需要新增需要的埠。
檢視防火牆狀態:systemctl status firewalld
防火牆新增埠:firewall-cmd —zone=public —add-port=61616/tcp —permanent
重啟防護牆:systemctl restart firewalld.service
或者直接關閉防火牆:systemctl stop firewalld.service

ActiveMQ的Web管理平臺是基於jetty的,在ActiveMQ的安裝目錄下conf檔案中有jetty.xml配置檔案,通過該檔案可以對Web管理平臺進行配置管理, 如:

<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
  <!-- the default port number for the web console -->
  <property name="host" value="0.0.0.0"/>
  <!--此處即為管理平臺的埠-->
  <property name="port" value="8161"/>
</bean>

<bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">
  <property name="name" value="BASIC" />
  <property name="roles" value="user,admin" />
  <!-- 改為false即可關閉登陸 -->
  <property name="authenticate" value="true" />
</bean>

通過jetty-realm.properties配置檔案可以對Web管理平臺的使用者進行管理:

# 在此即可維護賬號密碼,格式:
# 使用者名稱:密碼,角色
# Defines users that can access the web (console, demo, etc.)
# username: password [,rolename ...]
admin: admin, admin
user: 1234, user

ActiveMQ的Java示例

Maven管理的Jar包:

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.15.10</version>
</dependency>

Producer程式碼示例:

package com.demo.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class ProducerDemo {

  private static final String BORKER_URL = "tcp://ip:61616";
  private static final String QUEUE_NAME = "queue-test";

  public static void main(String[] args) throws Exception {
    // 建立連線工廠
    ActiveMQConnectionFactory activeMQConnectionFactory =
        new ActiveMQConnectionFactory("admin", "admin", BORKER_URL);
    // 建立連線物件
    Connection connection = activeMQConnectionFactory.createConnection();
    connection.start();
    // 建立會話
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    // 建立點對點傳送的目標Queue
    Queue queue = session.createQueue(QUEUE_NAME);
    // 建立訊息生產者
    MessageProducer producer = session.createProducer(queue);
    //    Topic topic1 = session.createTopic("topic-test");
    //    MessageProducer producer1 = session.createProducer(topic1);
    // 設定生產者的模式,有兩種可選 持久化 / 不持久化
    producer.setDeliveryMode(DeliveryMode.PERSISTENT);
    // 文字訊息
    TextMessage message = session.createTextMessage("Hello ActiveMQ message");
    // 傳送訊息
    producer.send(message);
    // 關閉連線
    producer.close();
    session.close();
    connection.close();
  }
}

執行之後可以在Web控制檯Queues tab下看到訊息:

img

Consumer程式碼示例:

package com.demo.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class ConsumerDemo {

  private static final String BORKER_URL = "tcp://192.168.0.242:61616";
  private static final String QUEUE_NAME = "queue-test";

  public static void main(String[] args) throws Exception {
    // 建立連線工廠
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(BORKER_URL);
    // 建立連線物件
    Connection connection = activeMQConnectionFactory.createConnection("admin", "admin");
    connection.start();
    // 建立會話
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    // 建立點對點消費的目標Queue
    Queue queue = session.createQueue(QUEUE_NAME);
    //    Topic topic1 = session.createTopic("topic-test");
    //    MessageConsumer consumer1 = session.createConsumer(topic1);
    // 建立訊息消費者
    MessageConsumer consumer = session.createConsumer(queue);
    // 接收訊息
    Message message = consumer.receive();
    if (message instanceof TextMessage) {
      System.out.println("收到文字訊息:" + ((TextMessage) message).getText());
    } else {
      System.out.println(message);
    }
    // 關閉連線
    consumer.close();
    session.close();
    connection.close();
  }
}

執行後可以看到訊息被消費:

img

SpringBoot中使用ActiveMQ的程式碼示例

Maven依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

yml配置檔案:

spring:
  activemq:
    broker-url: tcp://ip:61616
    user: admin
    password: admin

程式碼示例:

import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.core.JmsTemplate;

import javax.annotation.PostConstruct;

@SpringBootApplication
public class Producer {

  @Autowired private JmsTemplate jmsTemplate;

  @PostConstruct
  public void init() {
    ActiveMQTopic activeMQTopic = new ActiveMQTopic("topic-test");
    jmsTemplate.convertAndSend(activeMQTopic, "Hello SpringBoot ActiveMQ!");
  }

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

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.config.SimpleJmsListenerContainerFactory;

import javax.jms.ConnectionFactory;

@EnableJms
@SpringBootApplication
public class Consumer {

  @Bean
  public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory) {
    SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    factory.setPubSubDomain(true);
    return factory;
  }

  @JmsListener(destination = "topic-test", containerFactory = "myFactory")
  public void receive(String message) {
    System.out.println("Received Message: " + message);
  }

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

相關文章