重点:
- MQ在系统架构中扮演的角色 / 使用场景
- 削峰填谷
- 异步、解耦
- 延迟队列
- 高可用
- 各个MQ特性:比对多个MQ的特性
- kafka注册中心基于zk,支持自主选举;rocketmq注册中心基于name server,不支持选举
- kafka集群挂掉可以再选一个,支持读写;rocketmq挂了就只支持读操作,不支持写。
- rabbitmq社区活跃、文档良好,所以也有很多人用,发展很快——决定软件(事情)发展的更偏向资源,而不是性能、能力本身。
- 消息丢失与持久化配置、同步机制有关,说白了就是
- 时间换空间
- 性能换安全性
- 高性能的中间件需要软、硬件综合很强,比如MQ要想很大的吞吐量,对磁盘的性能是有要求的。
- 部署
- broker提供队列服务
- name server无论主从节点,每个节点都包含整个broker cluster的全部节点信息,包括主从节点
- customer配置全部name server信息,一个name server挂掉后,随机轮询其他name server。
- name server无状态的原因是,broker注册的信息很少量,一个name server完全扛得住,所以不需要像ZK一样的ZAB同步。
- 刷盘性能高?——使用零拷贝。提升性能可以从软件架构设计、算法、OS、硬件各个方面提升。
1. 概述
1.1 什么是MQ?为什么要用MQ?
MQ:MessageQueue,消息队列。 队列,是一种FIFO 先进先出的数据结构。消息由生产者发送到MQ进行排队,然后按原来的顺序交由消息的消费者进行处理。QQ和微信就是典型的MQ。
MQ的作用主要有以下三个方面:
- 异步例子:快递员发快递,直接到客户家效率会很低。引入菜鸟驿站后,快递员只需要把快递放到菜鸟驿站,就可以继续发其他快递去了。客户再按自己的时间安排去菜鸟驿站取快递。作用:异步能提高系统的响应速度、吞吐量——空间换时间。
- 解耦 :
- 服务之间进行解耦,才可以减少服务之间的影响。提高系统整体的稳定性以及可扩展性。
- 另外,解耦后可以实现数据分发。生产者发送一个消息后,可以由一个或者多个消费者进行消费,并且消费者的增加或者减少对生产者没有影响。
- 削峰例子:长江每年都会涨水,但是下游出水口的速度是基本稳定的,所以会涨水。引入三峡大坝后,可以把水储存起来,下游慢慢排水。作用:以稳定的系统资源应对突发的流量冲击。
1.2 MQ优缺点
上面MQ的作用也就是使用MQ的优点。 但是引入MQ也是有他的缺点的:
- 系统可用性降低系统引入的外部依赖增多,系统的稳定性就会变差。一旦MQ宕机,对业务会产生影响。这就需要考虑如何保证MQ的高可用。
- 系统复杂度提高引入MQ后系统的复杂度会大大提高。以前服务之间可以进行同步的服务调用,引入MQ后,会变为异步调用,数据的链路就会变得更复杂。并且还会带来其他一些问题。比如:如何保证消费不会丢失?不会被重复调用?怎么保证消息的顺序性等问题。
- 消息一致性问题A系统处理完业务,通过MQ发送消息给B、C系统进行后续的业务处理。如果B系统处理成功,C系统处理失败怎么办?这就需要考虑如何保证消息数据处理的一致性。
分布式场景下,微服务架构本身就具备上面所说的缺点,引入MQ只是放大、增强了这些缺点,并不是MQ带来了这些问题,当然也有MQ引入的问题,比如消息幂等性、消息丢失等。
1.3 MQ产品比较
常用的MQ产品包括Kafka、RabbitMQ和RocketMQ。我们对这三个产品做下简单的比较,重点需要理解他们的适用场景。
1.3.1 RocketMQ
- 淘宝内部的交易系统使用了淘宝自主研发的 Notify 消息中间件,使用 Mysql 作为消息存储媒介,可完全水平扩容,为了进一步降低成本,我们认为存储部分可以进一步优化
- 2011 年初,Linkin开源了 Kafka 这个优秀的消息中间件,淘宝中间件团队在对 Kafka 做过充分 Review 之后,Kafka 无限消 息堆积,高效的持久化速度吸引了我们,但是同时发现这个消息系统主要定位于日志传输,对于使用 在淘宝交易、订单、充值等场景下还有诸多特性不满足
- 为此我们重新用 Java 语言编写了 RocketMQ, 定位于非日志的可靠消息传输(日志场景也 OK),目前 RocketMQ 在阿里集团被广泛应用在订单, 交易,充值,流计算,消息推送,日志流式处理,binglog 分发等场景。
1.3.2 Kafka
Kafka 是 LinkedIn 开源的分布式发布-订阅消息系统,目前归属于 Apache 定级项目。Kafka 主要特 点是基于 Pull 的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8 版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互 联网服务的数据收集业务。
1.3.3 RabbitMQ
RabbitMQ 是使用 Erlang 语言开发的开源消息队列系统,基于 AMQP 协议来实现。AMQP 的主要特 征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP 协议更多用在企业 系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。
1.3.4 测试对比+
Kafka 的吞吐量高达 17.3w/s,不愧是高吞吐量消息中间件的行业老大。这主要取决于它的队列模式 保证了写磁盘的过程是线性 IO。此时 broker 磁盘 IO 已达瓶颈。
RocketMQ 也表现不俗,吞吐量在 11.6w/s,磁盘 IO %util 已接近 100%。RocketMQ 的消息写入内 存后即返回 ack,由单独的线程专门做刷盘的操作,所有的消息均是顺序写文件。
RabbitMQ 的吞吐量 5.95w/s,CPU 资源消耗较高。它支持 AMQP 协议,实现非常重量级,为了保 证消息的可靠性在吞吐量上做了取舍。我们还做了 RabbitMQ 在消息持久化场景下的性能测试,吞吐 量在 2.6w/s 左右。
在服务端处理同步发送的性能上,Kafka > RocketMQ > RabbitMQ。
对比了最简单的小消息发送场景,Kafka 暂时胜出。但是,作为经受过历次双十一洗礼的 RocketMQ,在 互联网应用场景中更有它优越的一面。
2. 快速实战
2.1 下载4.7.1版本
2.2 安装
- 配置环境变量
export JAVA_HOME=/home/wuyanzhao/jdk/11/jdk-11.0.12
export CLASSPATH=.:$JAVA_HOME/lib
export PATH=$JAVA_HOME/bin:$PATH
export ROCKETMQ_HOME=/home/wuyanzhao/rocketmq/rocketmq-4.9.3
export PATH=$ROCKETMQ_HOME/bin:$PATH
这个ROCKETMQ_HOME的环境变量是必须要单独配置的,如果不配置的话,启动NameSever和Broker都会报错。
这个环境变量的作用是用来加载$ROCKETMQ_HOME/conf下的除broker.conf以外的几个配置文件。所以实际情况中,可以不按这个配置,但是一定要能找到配置文件。
- 刷新环境变量source /etc/profile
- 修改 /usr/local/rocketmq/conf/broker.conf(启动时可选择该配置文件)
brokerClusterName = DefaultCluster brokerName = broker-a brokerId = 0 deleteWhen = 04 fileReservedTime = 48 brokerRole = ASYNC_MASTER flushDiskType = ASYNC_FLUSH autoCreateTopicEnable=true namesrvAddr=81.70.2.227:9876 brokerIP1=81.70.2.227 listenPort=10911
- 机器内存不够(一般针对虚拟机),修改/usr/local/rocketmq/bin/runserver.sh 与/usr/local/rocketmq/bin/runbroker.sh中JAVA_OPT关于内存的设置,我选择直接注释掉:
- vim runserver.sh
- vim runbroker.sh
2.3 运行
RocketMQ由以下这几个组件组成
- NameServer : 提供轻量级的Broker路由服务。
- Broker:实际处理消息存储、转发等服务的核心组件。
- Producer:消息生产者集群。通常是业务系统中的一个功能模块。
- Consumer:消息消费者集群。通常也是业务系统中的一个功能模块。
所以我们要启动RocketMQ服务,需要先启动NameServer。
2.3.1 启动NameServer
nohup bin/mqnamesrv ‐n 192.168.241.198:9876 &
启动完成后,在nohup.out里看到这一条关键日志就是启动成功了。并且使用jps指令可以看到有一个NamesrvStartup进程。
The Name Server boot success. serializeType=JSON
2.3.2 启动Broker
nohup sh bin/mqbroker ‐n localhost:9876 ‐c conf/broker.conf autoCreateTopicEnable=true &
启动完成后,同样是检查nohup.out日志,有这一条关键日志就标识启动成功了。 并且jps指令可以看到一个BrokerStartup进程。
The broker[VM-8-7-centos, 10.0.8.7:10911] boot success. serializeType=JSON and name server is localhost:9876
2.3.3 命令行快速验证
在RocketMQ的安装包中,提供了一个tools.sh工具可以用来在命令行快速验证RocketMQ服务。
首先需要配置一个环境变量NAMESRV_ADDR指向我们启动的NameServer服务。
export NAMESRV_ADDR='localhost:9876'
生产者
然后启动消息生产者发送消息:默认会发1000条消息
bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
我们可以看到发送消息的日志:
.....
SendResult [sendStatus=SEND_OK, msgId=C0A8E88007AC3764951D891CE9A003E7, offsetMsgId=C0A8E88000002A9F00000000000317BF, messageQueue=MessageQueue [topic=TopicTest, brokerName=worker1, queueId=1], queueOffset=249]
14:59:33.418 [NettyClientSelector_1] INFO RocketmqRemoting - closeChannel: close the connection to remote address[127.0.0.1:9876] result: true
14:59:33.423 [NettyClientSelector_1] INFO RocketmqRemoting - closeChannel: close the connection to remote address[192.168.232.128:10911] result: true
消费者
然后启动消息消费者接收消息
bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
启动后,可以看到消费到的消息。
......
ConsumeMessageThread_19 Receive New Messages: [MessageExt [brokerName=worker1, queueId=2, storeSize=203, queueOffset=53, sysFlag=0, bornTimestamp=1606460371999, bornHost=/192.168.232.128:43436, storeTimestamp=1606460372000, storeHost=/192.168.232.128:10911, msgId=C0A8E88000002A9F000000000000A7AE, commitLogOffset=42926, bodyCRC=1968636794, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=250, CONSUME_START_TIME=1606460450150, UNIQ_KEY=C0A8E88007AC3764951D891CE41F00D4, CLUSTER=DefaultCluster, WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 50, 49, 50], transactionId='null'}]]
而这个Consume指令并不会结束,他会继续挂起,等待消费其他的消息。我们可以使用CTRL+C停止该进程。
2.3.4 关闭服务
要关闭RocketMQ服务可以通过mqshutdown脚本直接关闭
# 1.关闭NameServer
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
3. 集群角色
一个完整的RocketMQ集群中,有如下几个角色
- Producer:消息的发送者;举例:发信者
- Consumer:消息接收者;举例:收信者
- Broker:暂存和传输消息;举例:邮局
- NameServer:管理Broker;举例:各个邮局的管理机构
- Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
我们之前的测试案例中,Topic是什么?topic='TopicTest'
现在你能看懂我们之前在broker.conf中添加的autoCreateTopicEnable=true这个属性的用处了吗?
- Message Queue:相当于是Topic的分区;用于并行发送和接收消息
4. 总结
- 了解MQ应用场景,解决的问题
- 主流MQ产品比较
- 简单部署 & mq中的角色概念