文章目錄
一. RocketMQ簡介
1.1 功能作用優點
-
RocketMQ作爲一款純java 分佈式 隊列模型的開源消息中間件,支持事務消息 順序消息 批量消息 定時消息 消息回溯等功能
-
支持發佈/訂閱(Pub/Sub)和點對點(P2P)消息模型
-
在一個隊列中可靠的先進先出(FIFO)和嚴格的順序傳遞 (RocketMQ可以保證嚴格的消息順序,而ActiveMQ無法保證)
-
支持拉(pull)和推(push)兩種消息模式 (Push好理解,比如在消費者端設置Listener回調;而Pull,控制權在於應用,即應用需要主動的調用拉消息方法從Broker獲取消息,這裏面存在一個消費位置記錄的問題(如果不記錄,會導致消息重複消費) )
-
單一隊列百萬消息的堆積能力 (RocketMQ提供億級消息的堆積能力,這不是重點,重點是堆積了億級的消息後,依然保持寫入低延遲)
-
支持多種消息協議,如JMS,MQTT等
-
分佈式高可用的部署架構,滿足至少一次消息傳遞語義(RocketMQ原生就是支持分佈式的,而ActiveMQ原生存在單點性)
-
提供docker鏡像用於隔離測試和雲集羣部署
-
提供配置、指標和監控等功能豐富的Dashboard
1.2 圖示
1.3 基本概念
- Producer:消息生產者
- Producer Group:消息生產者組,發送同類消息的一個消息生產組
- Consumer:消費者
- Consumer Group:消費同個消息的多個實例
- Tag:標籤,子主題(二級分類),用於區分同一個主題下的不同業務的消息
- Topic:主題
- Message:消息
- Broker:MQ程序,接收生產的消息,提供給消費者消費的程序
- Name Server:給生產和消費者提供路由信息,提供輕量級的服務發現和路由
二. 下載安裝與配置
環境:
- Windows 10
- Java :jdk1.8.0_191
- Apache Maven 3.6.1
1. 下載安裝
- 官網地址:http://rocketmq.apache.org/release_notes/release-notes-4.7.0/
- 選擇:Binary: rocketmq-all-4.7.0-bin-release.zip [PGP] [SHA512]下載
- 將下載後的zip文件解壓到非中文且沒有空格的目錄,比如:
D:\rocketmq-all-4.7.0-bin-release
2. RocketMQ 配置
2.1 配置環境變量
點擊新建
這裏給出變量名:ROCKETMQ_HOME
2.2 本地部署
2.2.1 啓動RocketMQ
- 進入RocketMQ 的bin目錄–啓動mq–如下圖 啓動命令是: start mqnamesrv.cmd
執行上面命令後如果報錯:找不到或無法加載主類,有兩種情況:- rocketmq解壓路徑有空格,放入非中文沒有空格的路徑
- Java 的環境變量有空格,把jdk的地址複製放入非中文沒有空格的路徑並且修改環境變量
注意jdk的版本最好是JDK 8 或者 JDK 9;版本太高會報錯
- 出現這個說明啓動成功,注意不要關閉窗口
2.2.2 啓動Broker程序
- 和上面方法一樣:cmd創建進入mq的bin目錄–執行命令:
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
如果報錯:
- 解決辦法:打開runbroker.cmd,然後將 %CLASSPATH% 加上英文雙引號 保存並重新執行start語句
- 啓動成功標誌
這個窗口不要關閉
2.2.3 部署RocketMQ插件
-
下載地址
- 官方下載地址:https://github.com/apache/rocketmq-externals.git(不推薦,同樣git方式下載也不推薦)
- 碼雲下載地址:https://gitee.com/mirrors/RocketMQ-Externals/
- CSDN下載地址:https://download.csdn.net/download/weixin_43330884/12273163(不要積分)
-
文件配置
下載完畢解壓後進入路徑:D:\RocketMQ-Externals\rocketmq-console\src\main\resources 配置application.properties文件
-
執行打包操作
進入 D:\RocketMQ-Externals\rocketmq-console 路徑,在地址欄直接 cmd 回車執行:
mvn clean package -Dmaven.test.skip=true
完成打包操作,此過程會聯網下載資源 -
啓動
打包完成後會生成 target 文件夾
cmd 進入 target:D:\RocketMQ-Externals\rocketmq-console\target
執行:java -jar rocketmq-console-ng-1.0.1.jar -
測試運行
localhost:8088
三. springboot整合RocketMQ
3.1 創建spring boot項目
詳細見博客:https://blog.csdn.net/weixin_43330884/article/details/104942624
3.2 配置pom.xml文件
<properties>
<java.version>1.8</java.version>
<rocketmq.version>4.7.0</rocketmq.version>
</properties>
<!--rocketMQ的jar包依賴-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${rocketmq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-common</artifactId>
<version>${rocketmq.version}</version>
</dependency>
3.3 配置 application.properties
# 消費者的組名
apache.rocketmq.consumer.PushConsumer=orderConsumer
# 生產者的組名
apache.rocketmq.producer.producerGroup=Producer
# NameServer地址
apache.rocketmq.namesrvAddr=127.0.0.1:9876
3.4 創建生產者和消費者
1.生產者:ProducerMsg.java
package cn.hp.springboot_day08_01.jms;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 生產者 --- 用戶產生消息
* @author hp
* @date 2020/03/27
*/
@Component
public class ProducerMsg {
/**
* 生成者組
*/
@Value("${apache.rocketmq.producer.producerGroup}")
private String producerGroup;
/**
* 從配置文件讀取 nameserver
*/
@Value("${apache.rocketmq.namesrvAddr}")
private String namesrvAddr;
/**
* 默認生產者
*/
private DefaultMQProducer producer;
/**
* 提供get方法,讓外部調用
* @return
*/
public DefaultMQProducer getProducer() {
return producer;
}
/**
* @PostConstruct
* 會在servlet初始化時執行,只執行1次
* 作用:讓當前方法在構造函數 之後,在init方法之前執行
*/
@PostConstruct
public void defaultMQProducer(){
//1. 創建一個默認的生產者對象--- 作用用於生成消息
producer = new DefaultMQProducer(producerGroup);
//2. 綁定生產者和 nameserver ,就是建立 和broker程序的關係
producer.setNamesrvAddr(namesrvAddr);
//3. 發送消息
try {
producer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
}
}
2.消費者:ConsumerMsg.java
package cn.hp.springboot_day08_01.jms;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 消息的 消費者
* @author hp
* @date 2020/03/27
*/
@Component
public class ConsumerMsg {
/**
* 讀取配置文件的 消費者
*/
@Value("${apache.rocketmq.consumer.PushConsumer}")
private String consumerGroup;
/**
* 從配置文件讀取 nameserver
*/
@Value("${apache.rocketmq.namesrvAddr}")
private String namesrvAddr;
/**
* 提供一個空的構造器
*/
public ConsumerMsg() {
}
/**
* 提供默認的消費者
*/
@PostConstruct
public void defaultMqConsumer() throws MQClientException {
//1.指定消費者 所消費的主題(隊列)和tag(2及標籤,用於過濾消息)
//從消費者組拿到1個消費者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
//說明:如果要消費所有 tag,用通配符*代替所有tag
consumer.subscribe("orderTopic","*");
//2.指定消費的策略 (從所有的消息開頭位置執行,還是從消息尾部執行)指定消費的順序
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//TODO ....
//註冊消息監聽器,用的lambda
consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
try {
for (MessageExt messageExt : list) {
System.out.println("messageExt: " + messageExt);//輸出消息內容
String messageBody = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);//讀取遠程配置的編碼爲utf-8
System.out.println("消費響應:msgId : " + messageExt.getMsgId() + ", msgBody : " + messageBody);//輸出消息內容
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍後再試
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消費成功
});
//3. 開啓監聽 消費消息
consumer.start();
}
}
3.4 創建controller運行測試
OrderController.java
package cn.hp.springboot_day08_01.controller;
import cn.hp.springboot_day08_01.jms.ProducerMsg;
import cn.hp.springboot_day08_01.utils.JsonData;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author hp
* @date 2020/03/27
*/
@RestController
@RequestMapping("/api")
public class OrderController {
@Autowired
private ProducerMsg producerMsg;
/**
* @param msg 發送的消息
* @param tag 二級標籤
* @return
*/
@RequestMapping("/order")
public Object order(String msg,String tag) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
//1.創建消息載體對象 Massage
Message message = new Message("orderTopic",tag,msg.getBytes());
//2.通過注入的 消息提供者對象發送消息
SendResult send = producerMsg.getProducer().send(message);
System.out.println("消息ID:"+send.getMsgId()+" 消息發送狀態:"+send.getSendStatus());
return JsonData.buildSuccess();
}
}
小工具JsonData.java:https://blog.csdn.net/weixin_43330884/article/details/105142483
測試:http://localhost:8080/api/order?msg=支付成功&tag=pay
四. Closing
一個簡單的案例應用,對於RocketMQ的學習不止於應用,更應於底層原理的探索。底層原理的探索也有助於提高對RocketMQ的整體理解與問題的定位,對適用場景的技術選型才更有把握。限於筆者的才疏學淺,對本文內容可能還有理解不到位的地方,如有闡述不合理之處還望留言一起探討!