一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
RocketMQ 总体架构
上图里的虚线表示数据同步过程,目的是保证数据的最终一致性。
从上图可以看出,RabbitMQ使用了一个注册中心作为发现与注册服务器,Producer向Broker发送消息,Consumer从Broker处接收消息,Broker使用主从结构来进行消息的存储(为了方便理解,我画了最经典的一主三从结构)。
接下来,我们将对每一个部分做更详细的拆解分析。
注册中心
在RocketMQ里,注册中心使用的是nameserver,原因是它具有稳定性高的特点,集群部署时单台nameserver挂掉并不会影响其它的,即使全部nameserver挂掉,也可以使用最近一次的注册信息来提供服务,不影响整体业务。
nameserver不会有频繁的读写,所以性能开销很少,稳定性也高。
在RocketMQ里,注册中心每隔10s会扫描一次所有的Broker,如果发现某个Broker已经连续2min没有发送心跳过来了,就认为其挂了,断开连接。此时注册中心会更新Topic与队列的对应关系,但不会通知Producer和Consumer。
Broker 架构
Broker通常以集群的形式运行,存储Producer发来的消息。
与注册中心的交互:
- 每个
Broker和所有的注册中心保持长连接 - 每30s向注册中心发送心跳,心跳里面有自身的
Topic信息
负载均衡设计:
Broker和Topic是多对多的关系,一个Topic会分布在多个Broker上,一个Broker里也会有多个Topic- 如果某个
Topic收到的消息很多,则应该给它配置多个队列,并让这些队列尽量平均分在多个Broker上
高可用设计: RocketMQ采用主从结构来保证高可用,Master负责消息的写入,多个Slave负责消息的读取,Slave定期从Master处同步数据,保证最终一致性,如果Master挂了,可以由Slave继续提供度服务,并重新投票选举出新的Master。
这里有两个关键细节需要注意:
- 一旦
Broker挂了,由于RocketMQ的心跳机制实现细节,Producer和Consumer最多30s才能够发现,在这一段时间里,发往这个Broker的消息都会发送失败,并且也没有办法消费消息 - RocketMQ保证的是最终一致性,所以会出现有一部分数据没来得及同步给Slave,Master就挂了的情况,但一旦Master回复之后,就又可以重新同步不一致的数据
高可靠设计:
- 所有收到的消息都会有同步和异步的刷盘机制,也就是将数据保存在硬盘上,可靠性很高
- 同步刷盘时,只有写入成功了才会返回成功
- 异步刷盘时,只有服务器宕机了才会丢失数据,这个概率是非常小的
读写性能:
- 使用了Linux系统的特性:以文件内存映射方式操作文件,避免read/write用户态和核心态的多次切换,性能很高
- 使用了顺序写IO,也会提高效率
- 读写分离,极大程度的缓解写锁和读锁的争用
除此之外,RocketMQ还提供了其他的功能及特性。
消息清理:
- 清理时机:默认是每天凌晨4点,或者磁盘空间不足时清理
- 磁盘空间阈值:默认阈值是85%,也就是当磁盘空间被占了超过85%之后会触发清理过程
- 消息写入磁盘文件后默认保存时间是72h
对此,我们也可以总结一下RocketMQ对服务器的硬件要求:
- 内存占用高
- 属于IO密集型应用,这种应用的特点是CPU附在高但使用率较低,大部分时间是在等待IO操作
- 硬盘要求高,这样数据持久化就会很快
消费者架构
与注册中心的交互:
- 一个
Consumer与注册中心的一台服务器保持长连接,并且会定时查询MQ的Topic配置信息,如果当前注册中心的服务器宕机,Consumer会自动与下一台服务器连接 - 在保持长连接的情况下,
Consumer每30s获取一次配置中心的所有Topic配置信息,如果某个Broker宕机,Consumer最多需要30s可以从配置中心感知到(如果读消息多次失败也可以感知到)
与Broker的交互:
Consumer及与之关联的Broker之间保持长连接Consumer默认每隔30s向与之关联的Broker发送心跳;Broker每10s扫描所有的长连接,如果某个连接2min内没有发送心跳数据,则关闭连接,并且通知消费者组里的其他消费者重新按照策略分配队列消费
负载均衡设计:
- 一个
Consumer集群里的每个Consumer只消费一个队列,如果某个Consumer挂了,则他的任务会被同组内Consumer接替
消费消息过程:
- RocketMQ采用拉模型,
Consumer不断拉取消息到本地来消费。拉取消息和消费消息是一个异步操作,所以就需要一个数据结构来存储拉下来的消息,这就是本地队列 - 默认每隔5s,RocketMQ就会将各个队列的消息消费进度存储到对应的
Broker上 - 消费者拉取队列里的消息时,会将消息拉取任务放在本地的线程执行队列里逐次执行,执行完毕后再将任务放到队尾,依次循环
生产者架构
与注册中心的交互:
- 一个
Producer与注册中心的一台服务器保持长连接,并且会定时查询MQ的Topic配置信息,如果当前注册中心的服务器宕机,Producer会自动与下一台服务器连接 - 在保持长连接的情况下,
Producer每30s获取一次配置中心的所有Topic的最新队列情况,如果某个Broker宕机,Producer最多需要30s可以从配置中心感知到(如果写入消息多次失败也可以感知到)
与Broker的交互:
Producer及与之关联的Broker之间保持长连接Producer默认每隔30s向与之关联的Broker发送心跳;Broker每10s扫描所有的长连接,如果某个连接2min内没有发送心跳数据,则关闭连接