RocketMQ介绍
(1)MQ是什么?为什么使用?
MQ其实就是Message Queue ——“消息队列”,是一种先进先出的数据结构。所以先进先出就是MQ的基本特点之一,它还有着其他的特点。
-
发布订阅
发布订阅模式是一种很好的设计模式,也是一种非常好的处理方式。不仅符合了我们的开闭原则,而且如果不发生阻塞的话,基本也可以当作是同步操作。
-
持久化
MQ也具有持久化功能,这让MQ不在只是一个场景下的工具,也能成为数据库那样存储核心数据。
-
分布式
支持分布式的部署,高可用,是一款高性能的中间件
也正是因为有了这些特点,我们的MQ也被运用到很多的场景。
(1)应用解耦
系统之间进行数据交互的时候,需要考虑时效性和稳定性。如果基于线程异步处理的话,可以保证用户体验但不无法保证稳定性;基于线程的同步处理的话,却无法去保证我们的性能了。这个时候就可以用MQ进行处理了。
以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
而使用MQ之后就可以进行解耦。比如物流系统发生故障,需要几分钟才能来修复,在这段时间内,物流系统要处理的数据被缓存到消息队列中,用户的下单操作正常完成。当物流系统回复后,补充处理存在消息队列中的订单消息即可,终端系统感知不到物流系统发生过几分钟故障。
在MQ中我们将生产消息的称为生产者(producer),消费信息的称为消费者(comsumer),在系统解耦之后,基于发布订阅的模式下,我们的生产者只需要关心自己的数据发出去了没有,消费者自己会去订阅。
(2)流量削峰
应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提高系统的稳定性和用户体验。而且出于经济的考量 ,我们的业务系统的QPS是1000,而只有某些特定日子才会达到流量最高峰,为了应对最高峰配置高性能的服务器显然是不划算,而用MQ来处理才更适合。
(3)数据分发
上面说过消息队列可以让系统之间数据交互进行解耦,这也是因为我们原本是上游-下游,现在是变成上游-MQ-下游的模式。通过MQ系统的生产方不需要去关心谁来使用数据,只需要将数据发送到消息队列,数据使用方直接在消息队列中消费自己订阅的消息就行。
(2)MQ的优缺点
优点:解耦、削峰、数据分发
缺点包含以下几点:
-
系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。
如何保证MQ的高可用?
-
系统复杂度提高
MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。
如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
-
一致性问题
A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。
如何保证消息数据处理的一致性?
关于这些缺点,不同产品的MQ有自己不同的解决办法。后面还会学习RocketMQ的特性。
(3)常见的MQ产品
下面是四款常见的MQ产品以及对比。
(4)RocketMQ的设计理念和目标
设计理念
RocketMQ的核心功能主要包括消息发送、消息存储、消息消费,设计理念主要是追求简单与性能第一,主要体现在如下三个方面。
(1)NameServer设计简单。没有使用常用的Zookeeper做注册中心,而是使用自研的NameServer进行元数据的管理(Topic路由信息)。而且Topic路由信息无须在集群之间保持强一致,追求最终一致性,能容纳分钟级的不一致。NameServer集群之间互不通信,也降低了实现的复杂难度,整体性能就会相比Zookeeper提升了很多。
(2)高效的IO存储机制。RocketMQ追求消息发送的高吞吐量,将消息存储文件设计成文件组的概念。组内单个文件大小固定,方便引人内存映射机制,所有主题的消息存储基于顺序写,极大地提供了消息写性能, 同时为了兼顾消息消费与消息查找,引入了消息消费队列文件与索引文件。
(3)容忍缺陷,提升性能。如果保证消息一定被消息消费者消费,并且保证只消费一次。RocketMQ没有解决,而是只保证消息被消费者消费,且允许被重复消费,简化了内核,提升了性能和高可用简单高效。关于重复消费问题就交给了用户自己在消息消费的时候实现幂等。
设计目标
(1)架构模式。和大多数消息中间件一样,采用发布订阅模式,基本的参与组件包括:消息发送者,消息服务器(消息存储)、消息消费、路由发现。
(2)顺序消息。RocketMQ可以保证消息到达消息服务器的顺序有序被消费。
(3)消息过滤。消息消费者可以对同一主题下的消息按照规则只消费自己感兴趣的消息。
- 消息在Broker(服务端)端过滤。Broker只将消息消费者感兴趣的消息发送给消息消费者。
- 消息在消息消费端过滤,过滤方式由消费者自己定义,缺点就是会有很多无用消息从Broker端传输过来。
(4)消息存储。对于消息中间件的考量一般有两个维度,消息堆积能力和消息存储性能。而RocketMQ追求消息存储的高性能,引入内存映射机制,所有主题的消息顺序存储在同一个文件中。同时为了避免消息无限在消息存储服务器中累积,引入了消息文件过期机制与文件存储空间报警机制。
(5)消息高可用性。RocketMQ在一系列故障的时候,通过同步刷盘机制可以确保不丢失消息,在异步刷盘模式下,会丢失少量消息。而且RocketMQ在后续的版本中将引入双写机制,来满足消息可靠性要求极高的场合。
(6)消息到达(消费)低延迟。RocketMQ 在消息不发生消息堆积时,以长轮询模式实现准实时的消息推送模式。
(7)确保消息必须被消费一次。RocketMQ通过消息消费确认机制(ACK)来确保至少被消费以此,但由于ACK消息有可能丢失等其他原因,RocketMQ无法做到消息只被消费一次,有重复消费的可能。
(8)回溯消息。回溯消息是指消息消费端已经消费成功的消息,由于业务要求需要重新消费消息。RocketMQ支持按时间回溯消息,时间维度可精确到毫秒,可以向前或前后回溯。
(9)消息堆积。消息中间件的主要功能是异步解锢,必须具备应对前端的数据洪峰,提高后端系统的可用性,必然要求消息中间件具备一定的消息堆积能力。 RocketMQ 消息存储使用磁盘文件(内存映射机制),并且在物理布局上为多个大小相等的文件组成逻辑文件组,可以无限循环使用。 RocketMQ 消息存储文件并不是永久存储在消息服务器端,而是提供了过期机制,默认保留3 天。
(10)定时消息。定时消息是指消息发送到Broker 后, 不能被消息消费端立即消费,要到特定的时间点或者等待特定的时间后才能被消费。
(11)消息重试机制。消息重试是指消息在消费时,如果发送异常,消息中间件需要支持消息重新投递,RocketMQ支持消息重试机制。
RocketMQ安装
Base
关于RocketMQ的安装,这里我采用的是在Linux环境进行安装,但是如果需要在Linux下安装的时候,首先你需要一台服务器或者是用自己的虚拟机,这里我是安装的虚拟机,通过MobaXterm进行连接使用。
环境:CentOS7
SSH连接工具:MobaXterm
Java:JDK1.8
安装步骤
因为是Linux环境下安装,所以我们采用二进制版本进行安装,安装包可以到官网进行下载。
这里使用的版本是rocketmq-all-4.4.0-bin-release.zip
(1)首先我们先使用MobaXterm进行连接我们的CentOS系统,如果这里不会的话,可以参考我的另一篇博客。MobaXterm连接本地CentOS7
(2)第二步我们还是要再确认我们的CentOS下具有JDK1.8的环境。
(3)第三步就是要将我们之前下载的rocketmq的二进制安装包复制到我们的虚拟机里面。这里我们可以通过MobaXterm直接进行复制。
(4)然后我们在找个地方进行解压,这里我创建了一个local/rocketmq文件夹进行解压,具体步骤指令参照图片进行。
解压之后,我们就可以该目录下多了一个文件夹。
(5)我们进入解压后的文件夹,可以看到里面的目录结构。
这里主要的三个文件夹介绍一下:
- bin:启动脚本,包括shell脚本和CMD脚本
- conf:实例配置文件 ,包括broker配置文件、logback配置文件等
- lib:依赖jar包,包括Netty、commons-lang、FastJSON等
RocketMQ启动测试
(1)启动NameServer
我们进入到了bin目录下,然后通过后台的形式进行启动
# 1.启动NameServer
nohup sh bin/mqnamesrv &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/namesrv.log
启动后可以通过上面第二条命令查看自己的nameserver是否启动成功。
(2)启动Broker
# 1.启动Broker
nohup sh bin/mqbroker -n localhost:9876 &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/broker.log
这里我们在查询日志的,会发现并没有生成日志文件。这或许可能是因为我们的Broker并没有启动成功,所以才会没有生成日志文件。
那么为什么会启动失败呢?
这是因为RocketMQ默认的虚拟机内存比较大,启动Broker如果因为内存不足失败,需要编辑如下两个配置文件,修改JVM内存大小。
# 编辑runbroker.sh和runserver.sh修改默认JVM大小
vi runbroker.sh
vi runserver.sh
- 参考设置:
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
进入bin目录下的这两个文件,可以发现,它们初始的内存大小分配的也太大了,我们手动进行修改。
修改后:
两个都修改后,我们对nameserver和broker都进行重新开启。
然后通过jps命令就可以看到,我们两个重要的节点都成功启动了。
关于关闭命令的话,我们可以使用以下两个命令。
# 1.关闭NameServer
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
消息发送测试
这里要进行消息发送测试,那么我们就先需要使用MobaXterm进行双窗口测试。
我们将左边的作为消息的发送者,右边的作为消息的接受者。关于这两者都需要进行一些操作。
发送信息
我们需要执行以下命令,注意如果在bin目录下的话,自己就要去掉命令中的bin目录下前缀。
# 1.设置环境变量
export NAMESRV_ADDR=localhost:9876
# 2.使用安装包的Demo发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
接受消息
# 1.设置环境变量
export NAMESRV_ADDR=localhost:9876
# 2.接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
测试结果
测试之后可以发现,我们的MQ成功的进行了通信。
参考资料
[1]https://www.bilibili.com/video/BV1L4411y7mn?p=2
[2]《RocketMQ技术内幕》 丁威
[3]网上零碎资料