vlambda博客
学习文章列表

消息队列系列(一)关于RocketMQ双主架构的设计思考




大家好,我是Idea!

最近在整理一些关于消息队列相关的知识点,所以开启了现在这个篇目。


本篇主要从RocketMQ的整体设计进行入手分析,不会有太过多源码层面的讲解,尽量通过一些图文的方式来带大家深入理解RocketMQ内部的一些设计细节。


https://rocketmq.apache.org/docs/motivation/

如果要设计一款消息组件,你会如何设计它的基本架构

在开始介绍RocketMQ的网络拓扑图之前,我们先从原始的设计开始构思,如果要让我们手动编码实现一款消息平台的产品,那么需要考虑哪些点?下边给出一些我个人的思考:


如何理解消息队列?

随着互联网流量的日渐爆发,高吞吐,高可用,高性能已经成了每个互联网公司内部都会遇到一个难题,而消息队列也是在互联网爆发之后所催生的一种产品。


ps: 其实很早的时候在操作系统的内部也有消息队列的设计,它被作为了进程间通信的方式之一,感兴趣的小伙伴可以去了解下System V消息队列的设计。

相关设计的原理图大致如下:

消息队列系列(一)关于RocketMQ双主架构的设计思考


更多底层细节可以去查看这篇文章,本文中就不做过多扩展:

https://www.cnblogs.com/Anker/archive/2013/01/07/2848869.html

消息队列在企业中落地的设计目的:

解耦:降低微服务间系统调用的强耦合问题

削峰:解决高并发请求对业务下游的冲击

异步:可以适用于一些异步场景

最终一致性:保证消息的消费结果能够达到最终一致。


参考Linux内核的消息队列设计,下边给出基于分布式环境部署的消息队列设计思路:

Version 1 单消息承载节点部署版本

消息队列系列(一)关于RocketMQ双主架构的设计思考

思考:如果消息承载点挂了怎么办?这没法解决高可用问题啊。


Version 2 多消息承载节点部署版本


消息队列系列(一)关于RocketMQ双主架构的设计思考

消息发送方往多个消息承载点发送同一条数据,保证单个节点挂了之后,还会有节点可以用于承载消息,然后consumer从消息承载点获取消息。

思考:多个承载点之间的数据一致性该如何解决?

通常解决这类设计问题的主要方案就是选出一个leader角色,所以不妨可以尝试使用主从的思维去设计优化。



Version 3 多消息承载节点+主从部署版本

消息队列系列(一)关于RocketMQ双主架构的设计思考

加入了主从关系之后,消息承载点的支撑能力开始有所提升,consumer可以从主节点和从节点读取消息,这样能保证两种情况下消息消费的可靠性:

  • 主节点挂了,消息可以从从节点读取

  • 从节点网络异常了,消息可以从主节点读取

思考:通常我们的主从集群部署都会存放在一个机柜内部从而降低网络延迟,但是如果一旦出现了机房断电,那么整个集群都会挂掉,该如何避免这类情况?


Version 4 多消息承载节点+多主从部署版本

消息队列系列(一)关于RocketMQ双主架构的设计思考

Producer发送消息的时候往两个主节点去写入,两套主从分别部署在不同的机柜,这样保证即使断电了也不影响线上业务的使用。



Version 5 多消息承载节点+多主从部署+注册中心版本



目前来看似乎可以将这套设计方案落地到实际企业应用中了。如果你看懂了上边几个版本的消息队列演进设计图,那么再来看下RocketMQ的集群部署网络拓扑图就不会觉得复杂了。


RocketMQ网络拓扑图

图中的NameServer其实就是我在前边提到的注册中心这个概念,Broker可以理解为我在前边提到的消息承载点概念。


下边是我从Github上摘选的一段关于RocketMQ集群部署架构模式下的描述:

RocketMQ 网络部署特点

  • NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。

  • Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId 来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。注意:当前RocketMQ版本在部署架构上支持一Master多Slave,但只有BrokerId=1的从服务器才会参与消息的读负载。

  • Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic 服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

  • Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,消费者在向Master拉取消息时,Master服务器会根据拉取偏移量与最大偏移量的距离(判断是否读老消息,产生读I/O),以及从服务器是否可读等因素建议下一次是从Master还是Slave拉取。

结合部署架构图,描述集群工作流程:

  • 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。

  • Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。

  • 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。

  • Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。

  • Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。


往 期 推 荐







整合