文章目录
一. 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的整体理解与问题的定位,对适用场景的技术选型才更有把握。限于笔者的才疏学浅,对本文内容可能还有理解不到位的地方,如有阐述不合理之处还望留言一起探讨!