此章节RocketMQ,作者因m2环境问题并未在本地环境搭建成功,可参考原文,后续再win环境重试后更新。
作者为冰河忠实粉丝,可以加入【冰河技术】知识星球获取源码,也可以关注【冰河技术】公众号,加入社群学习,学习氛围好,干货满满。
1 MQ的使用场景
MQ的英文全称是Message Queue,翻译成中文就是消息队列,队列实现了先进先出(FIFO)的消息模型。通过消息队列,我们可以实现多个进程之间的通信,例如,可以实现多个微服务之间的消息通信。MQ的最简模型就是生产者生产消息,将消息发送到MQ,消息消费者订阅MQ,消费消息。
MQ的使用场景通常包含:异步解耦、流量削峰。
1.1 异步解耦
关于异步的场景,我们这里举一个用户下单成功后,向用户发送通知消息,为用户增加积分和优惠券的场景。
1.1.1 同步耦合场景分析
如果是同步调用的场景,则具体业务为:当用户提交订单成功后,订单系统会调用通知系统为用户发送消息通知,告知用户下单成功,订单系统调用积分系统为用户增加积分,订单系统调用优惠券系统为用户增加优惠券。整个调用流程如下所示。
通过上图的分析,可以看到,用户调用订单系统下单时,总共会经过8个步骤。并且每个步骤都是紧密耦合在一起串行执行,如下所示。
此时,订单系统、通知系统、积分系统和优惠券系统是紧密耦合在一起的,订单系统下单、通知系统发通知、积分系统发积分和优惠券系统发优惠券,四个任务全部完成后,才会给用户返回提交订单的结果信息。
用户提交订单花费的总时间为调用订单系统下单的时间+订单系统调用通知系统发送通知的时间+订单系统调用积分系统发放积分的时间+订单系统调用优惠券系统发放优惠券的时间。
注意:这里为了更好的说明系统之间串行执行的问题,忽略了网络的延迟时间。
这种串行化的系统执行方式,在高并发、大流量场景下是不可取的。另外,如果其中一个系统异常或者宕机,势必会影响到订单系统的可用性。在系统维护上,只要任意一个系统的接口发生变动,订单系统的逻辑也要跟着发生变动。
1.1.2 异步解耦场景分析
既然在高并发、大流量场景下使用订单系统直接串行调用通知系统、积分系统和优惠券系统的方式不可取。其实,在用户提交订单的场景中,用户最关心的是自己的订单是否提交成功,由于下单时,订单系统会直接返回是否下单成功的提示。对于通知、积分和优惠券可以以异步的方式延后一小段时间执行。并且通知系统、积分系统和优惠券系统之间不存在必然的业务关联逻辑,它们之间可以以并行的方式执行。
所以,可以使用MQ将订单系统与通知系统、积分系统和优惠券系统进行解耦,用户调用订单系统的接口下单时,订单系统向数据库写入订单数据后,向MQ写入消息,就可以直接返回给用户下单成功的提示,此时通知系统、积分系统和优惠券系统都订阅MQ中的消息,收到消息后各自执行自身的业务逻辑即可。
当引入MQ进行异步解耦之后,用户调用订单系统的接口下单,订单系统执行完业务逻辑将订单数据入口,会向MQ发送一条消息,随后便直接返回用户下单成功的提示。通知系统、积分系统和优惠券系统会同时订阅MQ中的消息,当收到消息时,它们各自会执行自身的业务逻辑,并且它们是以并行的方式执行各自的业务逻辑。
从执行的时间线上可以看出,当引入MQ进行异步解耦之后,通知系统、积分系统、优惠券系统和订单系统回复响应都是并行执行的,大大提高系统的执行性能。
并且解耦后,任意一个系统异常或者宕机,都不会影响到订单系统的可用性。只要订单系统与其他系统提前约定好发送的消息格式和消息内容,后续任意一个系统的业务逻辑变动,几乎都不会影响到订单系统的逻辑。
1.2 流量削峰
MQ在高并发、大流量的场景下可以用作削峰填谷的利器,例如,12306的春运抢票场景、高并发秒杀场景、双十一和618的大促场景等。
在高并发、大流量业务场景下,瞬间会有大量用户的请求涌入系统,如果不对这些流量做处理的话,直接让这些流量进入下游系统,则很可能由于下游系统无法支撑如此高的并发而导致系统崩溃或宕机。为了解决这些问题,可以引入MQ进行流量的削峰填谷。
将流量发送到MQ中后,下游系统根据自身的处理能力进行消费即可。保证了下游系统的高可用性。
1.3 引用MQ后的注意事项
引入MQ最大的优点就是异步解耦和流量削峰,但是引入MQ后也有很多需要注意的事项和问题,主要包括:系统的整体可用性降低、系统的复杂度变高、引入了消息一致性的问题。
1.3.1 系统的整体可用性降低
在对一个系统进行架构设计时,引入的外部依赖越多,系统的稳定性和可用性就会降低。系统中引入了MQ,部分业务就会出现强依赖MQ的现象,此时,如果MQ宕机,则部分业务就会变得不可用。所以,引入MQ时,我们就要考虑如何实现MQ的高可用。
1.3.2 系统的复杂度变高
引入MQ后,会使之前的同步接口调用变成通过MQ的异步调用,在实际的开发过程中,异步调用会比同步调用复杂的多。并且异步调用出现问题后,重现问题,定位问题和解决问题都会比同步调用复杂的多。
并且引入MQ后,还要考虑如何保证消息的顺序等问题。
1.3.4 消息一致性问题
引入MQ后,不得不考虑的一个问题就是消息的一致性问题。这期间就要考虑如何保证消息不丢失,消息幂等和消息数据处理的幂等性问题。
1.4 MQ选型对比
目前,在行业内使用的比较多的MQ包含RabbitMQ、Kafka和RocketMQ。这里,我将三者的对比简单整理了个表格,如下所示。
消息中间件(MQ) | 优点 | 缺点 | 使用场景 |
---|---|---|---|
RabbitMQ | 功能全面、消息的可靠性比较高 | 吞吐量低,消息大量积累会影响性能,使用的开发语言是erlang,不好定制功能。 | 规模不大的场景 |
Kafka | 吞吐量最高,性能最好,集群模式下高可用 | 功能上比较单一,会丢失部分数据 | 日志分析,大数据场景 |
RocketMQ | 吞吐量高,性能高,可用性高,功能全面。使用Java语言开发,容易定制功能。 | 开源版不如阿里云上版,文档比较简单。 | 几乎支持所有场景,包含大数据场景和业务场景。 |
2 项目整合RocketMQ
2.1 源码编译安装RocketMQ
- 到链接https://github.com/apache/rocketmq/releases/tag/rocketmq-all-4.9.3 (opens new window)下载RocketMQ 4.9.3版本的源码。下载并解压后的源码如下所示。
- 打开cmd命令行,进入RocketMQ的解压目录,我这里是/Users/mawenda/Documents/mawenda/dev-tools/rocketmq-rocketmq-all-4.9.3目录,然后在cmd命令行输入如下命令开始编译打包。
mvn clean install -Dmaven.test.skip=true -Prelease-all
编译成功如下所示。
- 编译成功后,会在RocketMQ解压目录下的distribution目录下的target目录下生成RocketMQ的安装包,在我电脑上的目录就是:/Users/mawenda/Documents/mawenda/dev-tools/rocketmq-rocketmq-all-4.9.3/distribution/target。如下所示。
- rocketmq-4.9.3.zip Windows版本
- rocketmq-4.9.3.tar.gz Linux/mac版本
- 将编译出的安装包解压到某个目录下,此处为了方便,作者放到了项目目录下,如下所示。
- 在RocketMQ的解压目录下的conf目录下修改broker.conf文件,如下所示。
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# 自动创建Topic
autoCreateTopicEnable=true
# nameServ地址
namesrvAddr=127.0.0.1:9876
# 存储路径
storePathRootDir=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir
# commitLog路径
storePathCommitLog=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir/commitlog
# 消息队列存储路径
storePathConsumeQueue=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir/consumequeue
# 消息索引存储路径
storePathIndex=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir/index
# checkpoint文件路径
storeCheckpoint=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir/checkpoint
# abort文件存储路径
abortFile=/Users/mawenda/Documents/mawenda/data/rocketmq/dataDir/abort
此处需要自己根据实际情况,自行修改上述文件中配置的目录地址。
- 设置环境变量ROCKETMQ_HOME
此处作者为mac环境,就无需配置。
- 启动rocketmq
在安装目录执行如下命令。
- Windows
./bin/mqnamesrv.cmd
- Mac
nohup sh bin/mqnamesrv &
打印如下内容,说明RocketMQ的NameServer启动成功了。
2024-05-14 15:09:16 INFO FileWatchService - FileWatchService service started
2024-05-14 15:09:16 INFO main - The Name Server boot success. serializeType=JSON
- 启动Broker服务
- Win
./bin/mqbroker.cmd -n localhost:9876
- Mac
# 第一次启动不要后台启动
nohup sh bin/mqbroker -n localhost:9876 &
注意,此处作者在启动时,提示如下错误。
Error: VM option 'UseG1GC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
解决方式如下,编辑 bin/mqbroker 启动脚本,在执行启动命令前声明JVM参数,修改后如下所示。
# 增加改行代码,可根据个人配置修改jvm参数
export JAVA_OPT="${JAVA_OPT} -server -Xms1g -Xmx1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC"
if [ -z "$ROCKETMQ_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
ROCKETMQ_HOME=`dirname "$PRG"`/..
# make it fully qualified
ROCKETMQ_HOME=`cd "$ROCKETMQ_HOME" && pwd`
cd "$saveddir"
fi
export ROCKETMQ_HOME
sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@
打印如下信息,说明启动成功。
The broker[mawendadeMac-mini.local, 192.168.3.45:10911] boot success. serializeType=JSON and name server is localhost:9876
2.2 测试RocketMQ环境
RocketMQ内置了大量的测试案例,并且这些测试案例可以通过RocketMQ的bin目录下的tools.cmd命令进行测试。接下来,我们就使用RocketMQ自带的tools.cmd命令测试RocketMQ的环境。
- 启动生产者程序向RocketMQ发送消息。
重新打开cmd命令行,进入RocketMQ的bin目录,在命令行输入如下命令调用RocketMQ自带的生产者程序向RocketMQ发送消息。
set NAMESRV_ADDR=localhost:9876
# win环境使用 tools.cmd
tools.sh org.apache.rocketmq.example.quickstart.Producer
2.3 源码编译RocketMQ控制台
- 下载RocketMQ控制台源码并解压
https://github.com/apache/rocketmq-dashboard
- 进入到RocketMQ控制台源码解压目录的src/main/resources目录下,编辑application.yml文件,修改namesrvAddrs地址,去掉多余的namesrvAddrs地址。
rocketmq:
config:
# if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, default localhost:9876
# configure multiple namesrv addresses to manage multiple different clusters
namesrvAddrs:
- 127.0.0.1:9876
# 注释此处节点配置
# - 127.0.0.2:9876
- RocketMQ控制台启动时默认监听的端口是8080,由于我们项目中订单微服务监听的端口也是8080,所以,将RocketMQ控制台监听的端口修改为10003,修改前的配置如下所示。
server:
port: 10002
- 修改完application.yml文件后,打开cmd命令行,进入RocketMQ控制台源码的根目录,输入如下Maven命令开始编译RocketMQ控制台的源码。
mvn clean install -Dmaven.test.skip=true
- 编译完成后,会在源码根目录下生成target目录,并生成rocketmq-dashboard-1.0.1-SNAPSHOT.jar文件,如下所示。
- 重新打开cmd命令行,进入rocketmq-dashboard-1.0.1-SNAPSHOT.jar文件所在的命令,在命令行直接输入如下命令启动RocketMQ控制台程序。
java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar
- 验证
在浏览器中输入http://localhost:10002/#/, 展示控制台,如下所示。