RocketMQ架构

1,198 阅读7分钟

1. RocketMQ的架构模式

  1. NameServer: 负责管理集群里所有Broker的信息,让使用MQ的系统可以通过NameServer感知到集群里有哪些Broker
  2. Broker集群:在多台集群上部署Broker集群,使用主从架构实现数据多副本存储和高可用。负责存储消息,转发消息
  3. 生产者:向MQ发送消息的系统
  4. 消费者:从MQ获取消息的系统

2. RocketMQ的架构原理

--图片来源于网络 image.png

高可用

  • RocketMQ天然支持高可用,支持多主多从的部署架构。RocketMQ中没有master选举功能,所以通过配置多个master节点来保证RocketMQ的高可用
  • 如果master挂了,如果当前架构是一主多从,就意味着无法接收prooducer的消息了,但是消费者仍然能够继续消息。如果当前架构是多主多从,就可以保障当其中一个节点挂了,另外一个master节点仍然能够对外提供消息发送服务

读写分离机制

  • master节点负责接收事务请求(master也可以对外提供读请求),slave节点只负责接收读请求,并且slave接收mastet同步过来的数据,和master保持数据的一致

消息发送

  • 当为多主多从时,一条消息只会发送到其中一个主节点上,rocketmq对于多个master节点的消息发送,会做负载均衡,使消息可以平衡的发送到多个master节点上

消息消费

  • 上图中,两个master节点恰好可以平均的分发到两个消费者上,一个消费者消费一个master。如果此时只有一个消费者,那么这个消费者会消费两个master节点的数据
  • 由于每个master可以配置多个slave,所以其中一个master挂了,消息仍然可以被消费者从slave节点消费到

读写分离机制详解

  • 写入消息的时候,肯定是选择Master Slave写入的
  • 读取消息的时候,有可能从Master Broker获取,也有可能从Slave Broker获取,根据主服务器的消息堆积量来决定消费者是否向从服务器拉取消息消费

读取消息的原理:

  1. Broker 接收到消息消费者拉取请求,在获取本地堆积的消息量后,会计算服务器的消息堆积量是否大于物理内存的一定值(默认40%),如果是,则标记下次从 Slave服务器拉取,计算 Slave服务器的 Broker Id,并响应给消费者
  2. 当消费者收到拉取响应回来的数据后,会将下次建议拉取的 brokerID 缓存起来。以便下次拉取消息时,确定向哪个节点发送请求

总结:什么时候从slave拉取数据?

  1. master挂了
  2. 本次拉取的消息堆积量大于物理内存的40%

3. RocketMQ高可用的保证

1. 假设NameServer集群整体都故障了,那么还能生产和消费消息吗?

注册中心nameserver宕机后通过producer本地缓存,来进行数据的发送,如果producer重启了则不能发送数据

2. 如果Broker挂了,NameServer是如何感知的?(心跳机制)

Borker会定时(30s)向NameServer发送心跳

NameServer会定时(10s)运行一个任务,去检查一下各个Broker的最近一次心跳时间,如果某个Broker超过120s都没发送心跳了,就认为这个Broker已经挂掉了

salve Broker也会向所有的NameServer进行注册,salve Broker也会向所有的NameServer每30s发送心跳

3. Broker挂了,系统(生产者、消费者) 是怎么感知到的?

主要是通过拉取NameServer上Broker的信息

但是,因为Broker心跳、NameServer定时任务、生产者和消费者拉取Broker信息,这些操作都是周期性的,所以不会实时感知,所以会存在发送消息和消费消息失败的情况

RocketMQ的路由发现是非实时的。当topic对应的路由信息发生变化,NameServer并不会通知给客户端。而是由客户端定时拉取Topic对应的最新路由。不实时的路由发现引起的问题由客户端进行解决,保证了NameServer逻辑的简洁

4. 如果Broker和NameServer出现网络问题呢?

如果某个Broker没有宕机,而是该Broker和Namesrv之间的网络问题造成NameSrv认为某个Broker宕机了,Producer后续拿到新的路由信息后,其实此时Producer可以连通该Broker,此时Producer就不会给该Broker发送消息了

5. master broker挂了之后,消费端如何消费?

  1. 如果master broker挂了,消息就写入不了了,也就是client端不能再发送消息了
  2. 此时消费端会从slave读取消息

追问:如果此时master重启恢复了呢?

  1. 如果此时master恢复了,就从master继续拉取数据

追问:从master拉取数据,master怎么知道最新的offset呢?

  1. 此时最新的offset存储在slave上了。当master起来后,slave就会启动定时任务,找master要consumer group的offset, 但是这个时候master的offset是过期的,slave收到后跟自己的比较,发现是过期的,会直接丢弃
  2. 那master如何更新自己的offset呢? 此时client在内存中是保存了最新的offset,当以这个offset去master拉消息的时候,master就会发现自己的offset过期了,会用这个offset更新自己的offset
  3. 如果client也挂了呢?那这个offset就丢了,必须从过期的offset开始重新消费一遍

总结:当消费者得到master宕机通知后,会转向slave消费,但是slave不能保证master的消息100%都同步过来了,因此会有少量的消息丢失。但是消息最终不会丢的,一旦master恢复,未同步过去的消息会被消费掉

Broker宕机和重启后的情况:(如果master长时间不能恢复,会触发rebalance)

当某台主broker宕机后,主broker对应的队列分配的客户端(消费者)无法正常拉取消息,触发Rebalance, 负载均衡会触发队列重新分配,consumer会被映射到其他的queue上

待宕机的主broker重新启动后,下一次负载均衡又重新触发,恢复宕机前的负载映射

重复消费的问题:注意consumer如果在broker宕机期间重启,LocalFileOffsetStore(内存对象)中将不存在broker宕机前队列的消费进度,因此consumer只能从之前提交到broker的进度开始消费,如果consumer在broker宕机期间没有重启,可以使用LocalFileOffsetStore中保存的进度或者pullRequest中的进度进行拉取任务消费,这样子基本上不会造成消息重复消费

6. 如果Slave Broker挂了,有什么影响?

答案: 有一点影响,但是影响不太大

  1. 因为消息写入全部是发送到Master Broker的,消息获取也可以走Master Broker。
  2. 只不过有一些消息获取可能从Slave Broker获取的,如果Slave Broker挂了,此时无论消息写入还是消息拉取,还是可以继续通过Master Broker,对整体运行不影响
  3. 但是会导致所有读写压力都集中在Master Broker上

4. RocketMq如何保证高吞吐的?

  • 分布式存储海量消息
    • 每个Topic数据都是存储在多台Broker机器上的(存储海量消息的机制是分布式的存储)
    • 集群里多台Master Broker就足以存储海量的消息
  • 高并发
    • 系统流量分散在RocketMQ部署的多台机器上
    • 高并发场景下高QPS可以分散到多台Broker上扛下来。因为Topic数据会分散到多台Broker上
  • 可伸缩
    • 如果要抗更高的并发,存储更多的数据,可以在集群里添加更多的Broker机器,这样可以线性扩展集群