目錄
1、四大 MQ 介紹
在介紹 ActiveMQ 之前,我想先把當下存在且有一定認可度的四大 MQ 簡單做下說明。爲了便於整體觀看和比對,我列了如下表格:
名稱 | 單機吞吐量 | Topic 數量對吞吐量的影響 | 時效性 | 可用性 | 可靠性 | 優勢 |
---|---|---|---|---|---|---|
ActiveMQ | 萬級 | 大 | ms | 高,基於主從架構實現高可用性 | 有較低概率丟失數據 | |
RabbitMQ | 萬級 | 大 | um | 高,基於主從架構實現高可用性 | 有較低概率丟失數據 | 延遲極低 |
RocketMQ | 十萬級 | 小 | ms | 非常高,分佈式架構 | 經過參數優化配置,消息可以做到 0 丟失 | 可支持大量 Topic |
Kafka | 十萬級 | 大 | ms | 非常高,分佈式架構 | 經過參數優化配置,消息可以做到 0 丟失 | 吞吐量 |
其中,RabbitMQ
和 RocketMQ
是目前中小型公司用的比較多的。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 的消息傳遞類型
-
P2P(Point - to - Point):
點對點的,即一個生產者和一個消費者一一對應。一個生成者產生一個消息只能被一個消費者消費,消費完,消息就沒有了。如下圖:
-
P / S(Publish / Subscribe):
發佈 / 訂閱模式,即一個生產者產生消息並進行發送後,可以由多個消費者進行接收。發佈即接收,沒有消費者就自動結束,不會存在服務器裏。如下圖:
3、安裝配置
3.1 安裝
- 下載地址:https://activemq.apache.org/components/classic/download/
- 按平臺下載:
- 下載完後解壓;
- 進入 bin 目錄,window 選擇對應的 32 或 64 位文件夾後,直接雙擊“activemq.bat”即可完成啓動,liunx 下執行“./activemq start”完成啓動。然後打開瀏覽器輸入地址:http://localhost:8161/ 進入 index 頁,即表示成功,
默認賬號密碼均爲 admin
:
3.2 修改管理界面密碼
- 進入 conf 目錄下;
- 打開 jetty-realm.properties 文件;
- 找到 admin: admin, admin,格式爲 - 用戶名 : 密碼, [角色名, …]
- 想改成什麼密碼修改即可修改即可。
3.3 修改消息傳送密碼
- 進入 conf 目錄下;
- 打開 credentials.properties 文件;
- activemq.username 爲用戶名,activemq.password 爲密碼,按照自己的意願修改;
- 打開 activemq.xml 文件,在 上面添加如下配置:
<!-- 添加訪問 ActiveMQ 的賬號密碼 -->
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="${activemq.username}" password="${activemq.password}" groups="users,admins"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
- 還有一種
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();
}
}
運行示例:
- 先運行 Producer,然後在 ActiveMQ 界面會出先一個 Queue:
- 再運行 Consumer,控制檯會輸出:
接收到消息:Hello, world!
,且不會退出; - 多次運行 Producer,觀察 Consumer 會一直輸出。
4.2 P/S
同時創建 2 個消費者,分別修改生產者和消費者創建隊列對象
爲創建主題對象
:
// 5. 創建主題對象
Topic topic = session.createTopic("test-topic");
運行示例:
- 要先運行 2 個 Consumer;
- 再運行 Producer;
- 觀察兩個 Consumer 控制檯會分別輸出:
Topic1:接收到消息:Hello, world!
,Topic2:接收到消息:Hello, world!
,且不會退出; - 多次運行 Producer,觀察 Consumer 會一直輸出;
- ActiveMQ 管理界面如下:
5、整合到 SpringBoot
- 添加 pom 依賴:
<!-- activemq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
- 添加配置信息:
# activemq 基礎配置
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 生產環境設置密碼
spring.activemq.user=[根據實際情況填寫]
spring.activemq.password=[根據實際情況填寫]
spring.activemq.pool.enabled=false
- 編寫生產者工具類:
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);
}
}
- 添加消費者類:
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);
}
}
- 修改啓動類:
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!");
}
}
- 運行後查看結果,即可。