常见消息中间件之RocketMQ
前言
RocketMQ是一款分布式、队列模型的消息中间件,由阿里巴巴自主研发的一款具有低延迟、高并发、高性能和可靠性、万亿级别容量、可灵活扩展的消息中间件。它是阿里巴巴于2012年开源的第三代分布式消息中间件,早期开源2.X版本名为MetaQ;2015年迭代3.X版本,更名为RocketMQ,2016年贡献给Apache,经过一年多的孵化,最终成为Apache的顶级开源项目之一。RocketMQ是在Kafka的基础上发展起来的,它的诞生参考借鉴了Apache Kafka(后面的文章我会单独介绍Kafka)。起因是随着阿里巴巴电商业务的发展,他们发现Kafka对于具体的业务场景支持的不完善,需要一款更高性能的消息中间件,于是阿里巴巴的团队借鉴Kafka的设计思路,并结合自身“双十一”场景,自行开发了更贴合自己业务场景的RocketMQ,对Kafka进行了合理的扩展和API丰富,在可用性、可靠性以及稳定性等方面都有出色的表现。RocketMQ的消息路由、存储、集群划分等设计思路与Kafka都极其相似,唯一的不同是 RocketMQ 对于业务特性的支持更完善,所以更适用于业务场景。
1 专业术语
每一个技术框架,都有它的专有名词,RocketMQ的专业术语如下:
1)Producer:消息生产者,负责产生消息,一般由业务系统负责产生消息。
2)Consumer:消息消费者,负责消费消息,一般由后台系统负责异步消费。
3)Pull Consumer:Consumer的一种,需要主动请求Broker拉取消息。
4)Push Consumer:Consumer的一种,需要向Consumer对象注册监听。
5)Producer Group:生产者集合,一般用于发送一类消息。
6)Consumer Group:消费者集合,一般用于接受一类消息进行消费。
7)Broker:MQ消息服务(中专角色,用于消息存储和生产消费转发)。
2 能力与支持
1)支持集群模型、负载均衡、水平扩展能力,如下面我们要讲的集群架构。
2)亿级别的消息堆积能力。
3)采用零拷贝的原理、顺序写盘、随机读(索引文件)。
4)丰富的API使用。
5)代码优秀,底层通信框架采用Netty NIO框架。
6)NameServer代替Zookeeper。
7)强调集群无单点,可扩展,任意一点高可用,水平可扩展。
8)消息失败重试机制、消息可查询。
9)开源社区活跃度高,足够成熟(经过双十一考验)。
3 核心源码包及功能说明
如下图,我们看一下RocketMQ源码包的组成,这有利于我们以后更深入的学习。
1)rocketmq-broker 主要的业务逻辑,消息收发,主从同步,pagecache
2)rocketmq-client 客户端接口,比如生产者和消费者
3)rocketmq-common 公用数据结构等等
4)rocketmq-distribution 编译模块,编译输出等
5)rocketmq-example 示例,比如生产者和消费者
6)rocketmq-fliter 进行Broker过滤的不感兴趣的消息传输,减小带宽压力
7)rocketmq-logappender、rocketmq-logging日志相关
8)rocketmq-namesrv Namesrv服务,用于服务协调
9)rocketmq-openmessaging 对外提供服务
10)rocketmq-remoting 远程调用接口,封装Netty底层通信
11)rocketmq-srvutil 提供一些公用的工具方法,比如解析命令行参数
12)rocketmq-store 消息存储核心包
13)rocketmq-test 提供一些测试代码包
14)rocketmq-tools 管理工具,比如有名的mqadmin工具
4 集群架构
RocketMQ为我们提供了丰富的集群架构模型,包括单点模式、主从模式、双主模式以及生产上使用最多的双主双主模式(或者说多主多从模式),我们来看一下最经典的双主双从模式,如下图:
1)NameServer集群
NameServer集群作为超轻量级的配置中心,存储当前集群所有的Broker信息、Topic与Broker的对应关系。每个NameServer记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。NameServer只做集群元数据存储和心跳工作,功能简单,稳定性高。多个NameServer之间没有通信,不必保障节点间的数据强一致性,也就是说NameServer集群是一个多机热备的概念,单台NameServer宕机不影响其他NameServer工作。需要注意的是,及时整个NameServer集群宕机了,已经正常工作的Producer、Consumer、Broker仍然能正常工作,但新起的Producer、Consumer、Broker就无法工作。
NameServer采用的是心跳机制,具体如下:
a、单个Broker跟所有NameServer保持心跳请求,心跳间隔为30秒,心跳请求中包括当前Broker所有的Topic信息。需要注意的是,Broker向Namesrv发心跳时, 会带上当前自己所负责的所有Topic信息,如果Topic个数太多(万级别),会导致一次心跳中,就Topic的数据就几十M,网络情况差的话, 网络传输失败,心跳失败,导致Namesrv误认为Broker心跳失败。
b、NameServer会反查Broer的心跳信息, 如果某个Broker在2分钟之内都没有心跳,则认为该Broker下线,调整Topic跟Broker的对应关系。但此时NameServer不会主动通知Producer、Consumer有Broker宕机。
c、Consumer跟Broker是长连接,会每隔30秒发心跳信息到Broker。Broker端每10秒检查一次当前存活的Consumer,若发现某个Consumer 2分钟内没有心跳, 就断开与该Consumer的连接,并且向该消费组的其他实例发送通知,触发该消费者集群的负载均衡(rebalance)。
d、生产者每30秒从Namesrv获取Topic跟Broker的映射关系,更新到本地内存中。再跟Topic涉及的所有Broker建立长连接,每隔30秒发一次心跳。 在Broker端也会每10秒扫描一次当前注册的Producer,如果发现某个Producer超过2分钟都没有发心跳,则断开连接。
2)Producer集群
Producer集群就是消息生产者集群,它们在同一个生产者组Producer Group。Producer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
3)Consumer集群
Consumer集群就是消息消费者,它们在同一个消费者组Consumer Group。Consumer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
4)Broker集群
对于Broker来说,通常Master和Slave为一组服务,它们互为主从节点,通过NameServer与外部的Client端暴露统一的集群入口。Broker就是消息存储的核心MQ服务。
5 总结
RockerMQ作为国内顶级的消息中间件,其性能主要依赖于天然的分布式Topic/Queue,并且其内存与磁盘都会存储消息数据,借鉴了Kafka的“空中接力”概念,就是指数据不一定落地,RocketMQ提供了同步/异步双写、同步/异步复制的特性。在真正的生产环境中应该选择符合自己业务的配置。下面针对RocketMQ的高性能和瓶颈加以说明:
1)在实际生产环境中面临的主要瓶颈最终会落在IOPS上,也就是磁盘读写能力。当高峰期来临,每秒收发消息IOPS达到10W+消息,在云环境上,云环境的SSD物理存储显然和自建机房SSD有着很大的差距,这一点我们无论是从数据库的磁盘性能、还是搜索服务(ElasticSearch)的磁盘性能,都能给出准确的瓶颈点,单机IOPS达到1万左右就是云存储SSD的性能瓶颈,在这里我们也看到了“木桶短板原理”的效应,在真正的生产中,CPU的工作主要在等待IO操作,高并发下CPU资源接近极限,但是IOPS还是达不到我们想要的效果。
2)RocketMQ的性能已经足够好,但是在很多时候,我们的业务会有一些非核心的消息投递,可以进行消息中间件的业务拆分,把不重要的消息(允许消息丢失、非可靠性投递的消息)采用Kafka的异步发送机制,借住Kafka强大的吞吐量和消息堆积能力来做业务分流,以此缓解RocketMQ的性能瓶颈。