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物件模型
JMS訊息模型
在JMS標準中,有兩種訊息模型PTP(Point to Point)以及Publish/Subscribe(Pub/Sub)。
PTP,點對點訊息傳送模型
在點對點訊息傳送模型中,傳送者將訊息傳送給一個特殊的訊息佇列,該佇列儲存了所有傳送給它的訊息,消費者從這個佇列中獲取訊息。
PTP的特點:
- 每個訊息只有一個消費者,即一旦被消費,訊息就不再在訊息佇列中
- 傳送者和接收者之間在時間上沒有依賴性,也就是說當傳送者傳送了訊息之後,不管接收者有沒有正在執行,都不會影響到訊息被髮送到佇列
- 接收者在成功接收訊息之後需向佇列傳送確認收到通知
Pub/Sub,釋出/訂閱訊息傳遞模型
在釋出/訂閱訊息模型中,釋出者釋出一個訊息,該訊息通過topic傳遞給所有的客戶端。在這種模型中,釋出者和訂閱者彼此不知道對方,是匿名的且可以動態釋出和訂閱topic。在釋出/訂閱訊息模型中,目的地被稱為主題(topic),topic主要用於儲存和傳遞訊息,且會一直儲存訊息直到訊息被傳遞給客戶端。
Pub/Sub特點:
- 每個訊息可以有多個消費者
- 釋出者和訂閱者之間有時間上的依賴性。針對某個topic的訂閱者,它必須建立一個或多個訂閱者之後,才能消費釋出者的訊息,而且為了消費訊息,訂閱者必須保持執行的狀態。
- 為了緩和這樣嚴格的時間相關性,JMS允許訂閱者建立一個可持久化的訂閱,這樣就可以在訂閱者沒有執行的時候也能接收到釋出者的訊息
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解壓後的目錄結構:
在/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的啟動停止。
ActiveMQ服務
前面使用命令執行ActiveMQ,但最好的方式是將ActiveMQ作為服務啟動,使用system服務可以保證ActiveMQ在系統啟動時自動啟動。
建立ActiveMQ服務步驟:
- 建立一個systemd服務檔案:
vi /usr/lib/systemd/system/activemq.service
- 在服務檔案中新增以下內容
[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
- 產看Java安裝目錄:whereis java
- 設定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"
- 通過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
ActiveMQ的Web管理平臺
ActiveMQ自帶有Web管理平臺,預設使用8161埠,服務啟動後在瀏覽器輸入http://服務IP:8161/admin 即可進入,預設配置的賬戶admin,密碼也是admin。
如果服務啟動後頁面無法訪問可能是防火牆內需要新增需要的埠。
檢視防火牆狀態: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下看到訊息:
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();
}
}
執行後可以看到訊息被消費:
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);
}
}