👩🏽💻个人主页:阿木木AEcru
🔥 系列专栏:Docker容器化部署系列
💹每一次技术突破,都是对自我能力的挑战和超越。

一、RocketMQ介绍
1.1 发展史
RocketMQ是一个由阿里巴巴开源的分布式消息中间件,具有高性能、高可靠、高实时和分布式等特点。它支持事务消息、顺序消息、批量消息、定时消息和消息回溯等功能。阿里在2016年底捐赠给Apache开源基金会成为孵化项目,2017年正式成为了Apache顶级项目。 Apache RocketMQ 自诞生以来,因其架构简单、业务功能丰富、具备极强可扩展性等特点被众多企业开发者以及云厂商广泛采用。历经十余年的大规模场景打磨,RocketMQ 已经成为业内共识的金融级可靠业务消息首选方案,被广泛应用于互联网、大数据、移动互联网、物联网等领域的业务场景。
1.2 基本概念

这是最基础的消息模型,一个生产者,一个消息主题,一个消费者。 扩展后的消息模型如下:
 它拥有多个生产者,多个消息主题(每个主题中存在多个分区),两组消费者(每组中存在多个消费者)。
 它拥有多个生产者,多个消息主题(每个主题中存在多个分区),两组消费者(每组中存在多个消费者)。
- 为了消息写入能力的水平扩展,在这里的消息主题中被进行了分区,也就是俗称的"队列"。
- 为了消费能力的水平扩展,ConsumerGroup "消费者组" 的概念诞生了,可以存在多个消费者组,而每个消费者组中又存在着多个消费者。如何提高消费能力呢, 即广播模式,和集群模式。而图中使用的是集群模式。
这两种模式是有却别的
- 广播模式: 使用这种模式时, 所有的Consumer都会消费到每个 队列 中消息。例如 "MessageQueue 0" 队列中存在一条消息为 "你好" , 则所有 Consumer 都会消费到 "你好" 这条消息,相当于 村里的广播,一条消息,所有人都能听到。
- 集群模式: 使用这种模式时,每个 ConsumerGroup 中的 Consumer 是负载均衡消费的,例如图中 ConsumerGroup A 中的 两个 Consumer 是监听消费了 不同的 MessageQueue 。
1.3 部署模型

- 每个 Broker 与 NameServer 集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。
- Producer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取Topic路由信息,并向提供 Topic 服务的 Master 建立长连接,且定时向 Master 发送心跳。Producer 完全无状态。
- Consumer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取 Topic路由信息,并向提供 Topic 服务的 Master、Slave 建立长连接,且定时向 Master、Slave发送心跳。Consumer 既可以从 Master 订阅消息,也可以从Slave订阅消息。
1.4 集群工作流程
- 启动NameServer。NameServer启动后监听端口,等待Broker、Producer、Consumer连接,相当于一个路由控制中心。
- 启动 Broker。与所有 NameServer 保持长连接,定时发送心跳包。心跳包中包含当前 Broker 信息以及存储所有 Topic 信息。注册成功后,NameServer 集群中就有 Topic跟Broker 的映射关系。
- 创建 Topic 时需要指定该 Topic 要存储在哪些 Broker 上,也可以在发送消息时自动创建Topic。
- 生产者发送消息。启动时先跟 NameServer 集群中的其中一台建立长连接,并从 NameServer 中获取当前发送的 Topic存在于哪些 Broker 上,轮询从队列列表中选择一个队列,然后与队列所在的 Broker建立长连接从而向 Broker发消息。
- 消费者接受消息。跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,然后开始消费消息。
1.5 优势及使用场景
1.5.1 优势:
- 高性能:RocketMQ在消息的存储、传输和消费等各个环节都进行了优化,能够提供高性能的消息传递能力。
- 可靠性:RocketMQ采用了分布式架构,支持消息的持久化存储,具备高可靠性和数据安全性。
- 高吞吐量:RocketMQ支持集群部署,能够通过增加消费者和消息队列的方式来提高系统的吞吐量。
- 消息顺序性:RocketMQ支持消息的顺序消费,能够保证按照消息发送的顺序进行消费,适用于对消息顺序有严格要求的场景。
- 分布式事务:RocketMQ支持分布式事务消息的处理,能够保证在分布式环境下的事务的一致性和可靠性。
1.5.2使用场景:
- 
大规模分布式系统:RocketMQ适用于大规模分布式系统,能够进行高并发的消息传递,保证系统的稳定性和可靠性。 
- 
实时数据流处理:RocketMQ能够支持实时的数据流处理,适合用于实时监控、实时日志处理等场景。 
- 
异步任务处理:RocketMQ支持异步消息发送和消费,适用于异步任务处理、解耦系统组件等场景。 
- 
顺序消息处理:RocketMQ能够保证消息的顺序性,适用于需要严格保证消息顺序的场景,如订单处理、流程控制等。 
二、RocketMQ 部署
2.1 部署Name Server
2.1.1 拉取镜像
            
            
              powershell
              
              
            
          
          docker pull foxiswho/rocketmq:server-4.7.0
2.1.2 创建持久化挂载文件
            
            
              bash
              
              
            
          
          mkdir -p /usr/local/rockermq/rocketmq-server2.1.3 运行name server镜像
            
            
              powershell
              
              
            
          
          docker run -d \
--restart=always \
--name rmq-namesrver \
-p 9876:9876 \
-v /usr/local/rockermq/rocketmq-server/logs:/root/logs \
-v /usr/local/rockermq/rocketmq-server/store:/root/store \
-e "MAX_POSSIBLE_HEAP=100000000" \
foxiswho/rocketmq:server-4.7.0  \
sh mqnamesrv指令解析:
- --restart=always: 容器停止后自动重启
- --name rmqnamesrv: 容器的名称为"rmqnamesrv"
- -p 9876:9876: 将主机的9876端口映射到容器的9876端口
- -v /usr/local/rockermq/rocketmq-server/logs:/root/logs: 将主机的/usr/local/rockermq/rocketmq-server/logs目录挂载到容器的/root/logs目录
- -v /usr/local/rockermq/rocketmq-server/store:/root/store: 将主机的/usr/local/rockermq/rocketmq-server/store目录挂载到容器的/root/store目录
- -e "MAX_POSSIBLE_HEAP=100000000": 设置环境变量MAX_POSSIBLE_HEAP为100000000 sh mq-namesrver: 在容器内执行shmq-namesrver命令

2.1.4 开放防火墙端口
            
            
              powershell
              
              
            
          
          firewall-cmd --zone=public --add-port=9867/tcp --permanent
systemctl restart firewalld.service注: 如果使用的云服务器,还需要再安全组加上白名单。
2.2 部署Broker
2.2.1 拉取镜像
            
            
              bash
              
              
            
          
          docker pull foxiswho/rocketmq:broker-4.7.0
2.2.2 创建挂载文件以及配置文件
            
            
              ini
              
              
            
          
          mkdir -p /usr/local/rockermq/rocketmq-broker/conf
touch /usr/local/rockermq/rocketmq-broker/conf/broker.conf
vim /usr/local/rockermq/rocketmq-broker/conf/broker.conf
```broker.conf配置文件内容如下
```powershell
# 所属集群名字
brokerClusterName=DefaultCluster
# broker 名字,注意此处不同的配置文件填写的不一样,如果在 broker-a.properties 使用: broker-a,
# 在 broker-b.properties 使用: broker-b
brokerName=broker-a
# 0 表示 Master,> 0 表示 Slave
brokerId=0
# nameServer地址,多个的话用分号分割
# namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
namesrvAddr=[服务器ip]:9876
# 启动IP,如果 docker 报 com.alibaba.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.0.120:10909> failed
# 解决方式1 加上一句 producer.setVipChannelEnabled(false);
# 解决方式2 brokerIP1 设置宿主机IP,不要使用docker 内部IP (建议直接设置)
brokerIP1=[服务器ip]
# 在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
#defaultTopicQueueNums=4
# 是否允许 Broker 自动创建 Topic,建议线下开启,线上关闭 !!!这里仔细看是 false,false,false
#autoCreateTopicEnable=false
# 是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
#autoCreateSubscriptionGroup=false
# Broker 对外服务的监听端口
listenPort=10911
# 删除文件时间点,默认凌晨4点
deleteWhen=04
# 文件保留时间,默认48小时
fileReservedTime= 120
# commitLog 每个文件的大小默认1G
#mapedFileSizeCommitLog=1073741824
# ConsumeQueue 每个文件默认存 30W 条,根据业务情况调整
#mapedFileSizeConsumeQueue=300000
# destroyMapedFileIntervalForcibly=120000
# redeleteHangedFileInterval=120000
# 检测物理文件磁盘空间
#diskMaxUsedSpaceRatio=88
# 存储路径
# storePathRootDir=/home/ztztdata/rocketmq-all-4.1.0-incubating/store
# commitLog 存储路径
# storePathCommitLog=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/commitlog
# 消费队列存储
# storePathConsumeQueue=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/consumequeue
# 消息索引存储路径
# storePathIndex=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/index
# checkpoint 文件存储路径
# storeCheckpoint=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/checkpoint
# abort 文件存储路径
# abortFile=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/abort
# 限制的消息大小
maxMessageSize=65536
# flushCommitLogLeastPages=4
# flushConsumeQueueLeastPages=2
# flushCommitLogThoroughInterval=10000
# flushConsumeQueueThoroughInterval=60000
# Broker 的角色
# - ASYNC_MASTER 异步复制Master
# - SYNC_MASTER 同步双写Master
# - SLAVE
brokerRole=ASYNC_MASTER
# 刷盘方式
# - ASYNC_FLUSH 异步刷盘
# - SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
# 发消息线程池数量
# sendMessageThreadPoolNums=128
# 拉消息线程池数量
# pullMessageThreadPoolNums=1282.2.3 运行broker镜像
            
            
              bash
              
              
            
          
          docker run -d  \
--restart=always \
--name rmq-broker \
--link rmq-namesrv:namesrv \
-p 10911:10911 \
-p 10909:10909 \
-v  /usr/local/rockermq/rocketmq-broker/logs:/root/logs \
-v  /usr/local/rockermq/rocketmq-broker/store:/root/store \
-v  /usr/local/rockermq/rocketmq-broker/conf:/opt/rocketmq-4.7.0/conf \
-e "NAMESRV_ADDR=namesrv:9876" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-e "autoCreateTopicEnable=true" \
foxiswho/rocketmq:broker-4.7.0  \
sh mqbroker -c /opt/rocketmq-4.7.0/conf/broker.conf指令解析:
- --restart=always: 如果容器发生错误或被停止,自动重新启动。
- --name rmq-broker: 容器的名称为rmq-broker。
- --link rmq-namesrv:namesrv: 将rmq-namesrv容器链接到rmq-broker容器,以便它们可以相互通信。
- -p 10911:10911: 将主机的10911端口映射到容器的10911端口,用于消息存储。
- -p 10909:10909: 将主机的10909端口映射到容器的10909端口,用于管理控制台。
- -v /usr/local/rockermq/rocketmq-broker/logs:/root/logs: 将主机的日志目录挂载到容器内的日志目录。
- -v /usr/local/rockermq/rocketmq-broker/store:/root/store: 将主机的存储目录挂载到容器内的存储目录。
- -v /usr/local/rockermq/rocketmq-broker/conf:/opt/rocketmq-4.7.0/conf: 将主机的配置文件目录挂载到容器内的配置文件目录。
- -e "NAMESRV_ADDR=namesrv:9876": 设置环境变量NAMESRV_ADDR为namesrv:9876,指定NameServer的地址和端口。
- -e "MAX_POSSIBLE_HEAP=200000000": 设置环境变量MAX_POSSIBLE_HEAP为200000000,指定Broker的堆内存大小。
- -e "autoCreateTopicEnable=true": 设置环境变量autoCreateTopicEnable为true,启用自动创建Topic功能。
- foxiswho/rocketmq:broker-4.7.0: 使用名为foxiswho/rocketmq的镜像的broker-4.7.0标签。
- sh mqbroker -c /opt/rocketmq-4.7.0/conf/broker.conf: 在容器内执行启动Broker的命令,并指定配置文件的路径。

2.2.4 开放防火墙端口
            
            
              powershell
              
              
            
          
          firewall-cmd --zone=public --add-port=10911/tcp --permanent
systemctl restart firewalld.service注: 如果使用的云服务器,还需要再安全组加上白名单。
实际上部署到这已经是可以使用了,为了更方便的查看消息的发送和消费,最好再多部署一个RocketMQ的客户端界面。
2.3 部署client客户端界面
2.3.1 拉取并运行镜像
            
            
              diff
              
              
            
          
          docker run -itd -e "JAVA_OPTS=-Drocketmq.namesrv.addr=[服务器ip]:9876 \
-Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 8082:8080 \
--name rmq-client \
-t styletang/rocketmq-console-ng:latest如果镜像不存在 则会自动拉取并启动 指令解析:
- -itd:创建一个交互式容器,并在后台运行。
- -e "JAVA_OPTS=-Drocketmq.namesrv.addr=[服务器ip]:9876 \ -Dcom.rocketmq.sendMessageWithVIPChannel=false":设置容器内的环境变量,指定RocketMQ的地址和端口。
- -p 8082:8080:将容器内的8080端口映射到主机的8082端口。
- --name rmq-client:给容器指定一个名称为rmq-client。
- -t styletang/rocketmq-console-ng:latest:指定容器使用的镜像为styletang/rocketmq-console-ng:latest。

ps: 这里需要注意的是,-Drocketmq.namesrv.addr=[服务器ip]:9876 这段启动参数的中的服务器ip最好与 broker配置文件中的 ip 一致,否则会导致连接不上broker服务
2.3.2 客户端界面展示

三、SpringBoot整合RocketMQ
3.1 引入依赖
            
            
              xml
              
              
            
          
          <!--SpringBoot基础依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- rocketMq -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!-- hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-bom</artifactId>
            <version>5.8.10</version>
            <type>pom</type>
        </dependency>
		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>3.2 yml配置文件
            
            
              yml
              
              
            
          
          server:
  port: 8888
rocketmq:
  name-server: [部署的ip]:98763.3 demo目录

3.4 config目录下的类
3.4.1 RocketMqConfig------mq配置文件
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.config;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
 * mq配置文件
 */
@Configuration
public class RocketMqConfig {
	@Autowired
	private RocketMqProductAdapter rocketMqProductAdapter;
	@Autowired
	private RocketMqConsumeAdapter rocketMqConsumeAdapter;
	//初始化生产者
	@Lazy
	@Bean(destroyMethod = "destroy")
	public RocketMQTemplate productionPackagingConfirmMqTemplate() {
		return rocketMqProductAdapter.getTemplateByTopicName(RocketMqConstant.MQ_TEST_TOPIC);
	}
}3.4.2 RocketMqConstant------公用属性配置类
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.config;
public class RocketMqConstant {
	// 延迟消息 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h (1-18)
	/**
	 * 延迟发送时间 2 = 5s
	 */
	public static final int DELAY_LEVEL = 2;
	/**
	 * 默认发送消息超时时间
	 */
	public static final long TIMEOUT = 3000;
	/**
	 * 测试队列
	 */
	public static final String MQ_TEST_TOPIC = "mq-test-topic";
}3.4.3 RocketMqConsumeAdapter------消费者配置类
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.config;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
 * 获取消费者
 */
@Configuration
@RequiredArgsConstructor
public class RocketMqConsumeAdapter {
	private final RocketMQMessageConverter rocketMqMessageConverter;
	
	@Value("${rocketmq.name-server:}")
	private String nameServer;
	
	//消费者
	public RocketMQTemplate getTemplateByConsumeTopicName(String topic) {
		RocketMQTemplate mqTemplate = new RocketMQTemplate();
		DefaultLitePullConsumer sender = new DefaultLitePullConsumer(topic);
		sender.setMessageModel(MessageModel.CLUSTERING);
		sender.setNamesrvAddr(nameServer);
		mqTemplate.setConsumer(sender);
		mqTemplate.setMessageConverter(rocketMqMessageConverter.getMessageConverter());
		return mqTemplate;
	}
}3.4.4 RocketMqProductAdapter------生产者配置类
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.config;
import cn.hutool.core.util.IdUtil;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
 * 获取生产者
 */
@Configuration
@RequiredArgsConstructor
public class RocketMqProductAdapter {
	private final RocketMQMessageConverter rocketMqMessageConverter;
	@Value("${rocketmq.name-server:}")
	private String nameServer;
	//初始化连接rockerMq的客户端名称
	@PostConstruct
	public void init() {
		System.setProperty("rocketmq.client.name", "AEcru"+ IdUtil.getSnowflakeNextIdStr() + "@" + System.currentTimeMillis());
	}
	//创建生产者
	public RocketMQTemplate getTemplateByTopicName(String topic) {
		RocketMQTemplate mqTemplate = new RocketMQTemplate();
		DefaultMQProducer producer = new DefaultMQProducer(topic);
		producer.setNamesrvAddr(nameServer);
		producer.setRetryTimesWhenSendFailed(2);
		//默认发送消息超时时间
		producer.setSendMsgTimeout((int) RocketMqConstant.TIMEOUT);
		//最大消息大小(如需兼容大消息内容,还需修改borker服务配置文件)
		producer.setMaxMessageSize(1024 * 1024 * 1024);
		mqTemplate.setProducer(producer);
		mqTemplate.setMessageConverter(rocketMqMessageConverter.getMessageConverter());
		return mqTemplate;
	}
}3.5 controller------Product生产者
为了方便,我这边就想生产者写到了Controller这边,方便后续的测试调用,实际开发中需要按照实际业务来进行编写。
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.controll;
import com.itbanana.rocketmqdemo.config.RocketMqConstant;
import lombok.AllArgsConstructor;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
/**
 * @author lengleng
 * @date 2018/12/16
 */
@RestController
@AllArgsConstructor
@RequestMapping("/mq-test")
public class RocketMqController {
	private final RocketMQTemplate pigProductTestTemplate ;
	/**
	 * 发送消息到mq
	 * @param msg 发送消息内容
	 */
	@PostMapping("/send")
	public void send(@RequestBody String msg){
		sendMq(msg,pigProductTestTemplate, RocketMqConstant.MQ_TEST_TOPIC,"消息备注信息");
	}
	/**
	 * 发送数据
	 *
	 * @param rvo
	 * @param topic
	 * @return
	 */
	private  void sendMq(Object rvo, RocketMQTemplate rocketMqTemplate, String topic, String msg) {
		SendStatus sendStatus = rocketMqTemplate.syncSend(topic, new GenericMessage<>(rvo), RocketMqConstant.TIMEOUT).getSendStatus();
		if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
			throw new RuntimeException(msg);
		}
	}
}3.6 listener------Consumer消费者
            
            
              java
              
              
            
          
          package com.itbanana.rocketmqdemo.listener;
import com.itbanana.rocketmqdemo.config.RocketMqConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/**
 * 消费者
 */
@Slf4j
@Component
@RequiredArgsConstructor
@RocketMQMessageListener(topic = RocketMqConstant.MQ_TEST_TOPIC, consumerGroup = RocketMqConstant.MQ_TEST_TOPIC, messageModel = MessageModel.CLUSTERING)
public class MqConsumerImpl implements RocketMQListener<String>{
	@Override
	public void onMessage(String message) {
		log.info("listener 消费数据:{}", message);
	}
}3.7 启动项目后展示
3.7.1 开发工具控制台

3.7.2 RocketMQ界面



3.8 测试使用
3.8.1 接口请求

3.8.2 RocketMQ界面客户端


3.8.3 控制台打印结果

四、结尾
- 感谢您的观看! 如果本文对您有帮助,麻烦用您发财的小手点个三连吧!您的支持就是作者前进的最大动力!再次感谢!
- 如果觉得本专栏还不错的话可以点个订阅!
- 下期将推出Docker部署Mysql主从+SpringBoot整合ShardingJdbc实现分库分表相关内容