目录
1.RocketMQ的集群部署模式
1.1 单Master模式
Broker单节点架构,一旦master宕机,整个MQ服务将处于不可用状态。
1.2 多Master模式
多个master节点组成集群,单个master一旦发生了宕机,对MQ服务没有较大影响。
优点:性能高、同一个Topic主题可以分布在多个master,横向拓展好。
缺点:单个master宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性受影响。
1.3 多master多slave模式(异步复制)
从节点异步复制master节点的消息数据,但是从节点不提供消息的写入。所以从节点对生产者无感,但是在master繁忙的时候替master分担读/消费的任务。
使用异步复制可能会导致数据的丢失。
1.4 多master多slave模式(同步复制-异步刷盘)(推荐)
相比于异步复制,性能损耗在10%,但是能够接收。消息不会丢失,性能也良好。异步刷盘的方式提高了RocketMQ的吞吐量。
2.5 Dledger
RocketMQ在4.5版本推出了Dlegder模式,类似于ZK的集群选举模式,暂不推荐使用。// TODO
2.刷盘策略与数据同步
2.1 同步刷盘
生产者发送的每一条消息都要保存到Broker磁盘成功后才返回高速生产者成功,这种方式不会导致消息丢失,但是性能很差。
2.2 异步刷盘
生产者发送的每一条消息不是立即刷到磁盘,而是缓冲到一个Buffer中就快速返回成功。随后,后台任务再将Buffer中的数据刷回磁盘。(定时刷+阈值触发自动刷)
2.3 同步复制
复制指的是master节点上的消息数据复制给slave从节点的过程。同步复制代表生产者发送的每一条消息都至少同步复制到一个从节点后才返回告诉生产者成功。这种方式可靠性很高,不会造成消息丢失。
2.4 异步复制
相比于同步复制,就是一个异步任务的执行,性能略高,但是存在消息复制阶段丢失问题。
现在RocketMQ推荐的做法是:主从节点采用同步复制、节点内使用异步刷盘的模式组合。
3.Producer发送消息的高可用设计
1.首先Topic X在两个Master节点的Broker上都有分别4个Message Queue。
2.默认使用轮询的方式进行队列和Broker的选择。例如选中了Broker A的Q4。
3.如果发送成功,则正常返回,结束。
4.如果发送失败就会触发重试机制
(消息最大重试次数是2次),并选择使用哪一种重试策略。
5.重试策略有2种:开启和不开启故障延迟机制。(默认不开启)
6.如果不开启故障延迟机制,那么重试发送就会轮询选择刚才失败的那个Broker的下一个队列,例如Broker A的Q1。(这种方式的缺点是有可能重试会再一次失败,因为如果第一次失败了大部分情况是这个Broker有问题了,所以当第二次选择这个Broker的其他队列时,大概率也会失败。)
7.如果开启了故障延迟机制,那么在消息第一次发送失败后就会将该Broker置为不可用列表,转而重新选择Broker。(这种方式的缺点是,一旦所有的Broker都失败了,那么这个客户端将无法发送消息。)
3.1 Broker故障延迟机制(故障规避)
生产者一旦发生了消息发送失败,就会将这个Broker置为不可用,在后续的一段时间内都不会出现在队列、Broker的选择列表上。
4.Consumer消费消息的高可用设计
消息消费者端的消费可以从Master节点读取,也可以从Slave从节点读取。当Master不可用或者系统繁忙的时候就可以从Slave节点读取消息,这样也是高可用的一个设计。
Master服务繁忙:需要拉取的消息已经超过Broker常驻内存的大小,表示服务器繁忙,转而高速消费者从Slave节点读取数据。
Consumer的另一个高可用体现在重试上,一旦某个消息消费失败,就会进入重试队列
中,重试队列是基于消费者组Group的,因为不同的Group消费进度互不干扰。
RocketMQ消息的重试时间间隔不是固定的,而是每一次重试都会时间递增。例如第一次重试是10S,下一次可能就是30s,在下一次就是1分钟了。最多重试16次。
当超过16次或指定次数后,仍然没有消费成功,则进入死信队列。死信队列的数据3天后删除。
4.1 Consumer负载
一个Message Queue只能有一个消费者,但是一个消费者可以订阅负载多个消息队列。一般队列数量 >= 消费者数量比较好,如果消费者数量 > 队列数量,即便再加消费者页不起作用。如果消息队列数量 > 消费者数量,那么消费者服务器的启动和退出都会触发重新负载。
Consumer负载有两种模式:
5.Reblance
消费者端触发Rebalance的触发点有3个:
- 消费者启动
- 消费者加入or退出
- 每隔10s定时触发
RocketMQ的Topic的每一个队列有且只能分配一个消费者进行消费。例如,TopicA分布在2个Broker节点上,每一个Broker节点都有默认4个这个TopicA的队列,如果此时只有1个消费者,那么这个消费者就会分摊8个队列的任务,如果此时加入一个消费者,那么每一个消费者就会平均分配4个。当队列数量和消费者数量持平的时候,此时再加入消费者,是不会触发Reblance的,多出来的消费者没有任何任务负担。
另外,Consumer端每隔一段时间就会进行队列的负载重新计算。Consumer拉取消息一次性拉取32条记录放到消费者线程池里进行消费,如果消费者的缓冲区发生了堆积,就会触发Consumer的Pull动作的流控。
Consumer消费者线程池是并发地进行任务池中的任务,如果是顺序消息,那么线程池的线程会和Queue做绑定。
6.RocketMQ的堆外内存使用
RocketMQ的CommitLog、ConsumQueue等通过mmap技术,当接收到消息时写入内存映射文件,然后消费的时候通过内存映射读取。RocketMQ还提供了另外一套机制来优化效率:堆外内存。
TransientStorePool:RocketMQ创建一个和CommitLog大小一样的ByteBuffer内存缓冲,用来临时存储数据,数据先写入堆外内存,然后后台线程定时从buffer中复制到PageCache中进行持久化刷盘。这种方式需要在RocketMQ的配置文件中手动开启,且Broker模式必须是异步刷盘模式。
两种刷盘方式比较:
7.消息积压
7.1 Producer太快
- 控制消息的生产速度
- 调整业务逻辑,减少没必要的消息
- 处理逻辑较长和较短的消息不要放在一个队列或者Topic
7.2 Consumer太慢
- 分析消费逻辑的特点,选择性的增加消费者的线程数
- 如果队列数量 > consumer数量,可以添加consumer数量来提高消费能力
- 如果队列数量 <= consumer数量,可以选择增大队列数量,和consumer数量达成一致
- consumer端修改逻辑,进行消息的搬迁和转移到新的topic中,增大新的队列数量
- 修改consumer单次拉取消息的数量,默认是32个
- 丢弃消息、重置消费位点(有时候,这种粗暴的方案或许反而是最高效的)
8.消费幂等
- 基于数据库去重表,对消息ID、业务唯一标识进行主键、UK的设定来保证幂等。
- 通过数据库的版本号来做。
- 分布式锁进行保证。
- 基于第三方组件的特性,例如ZK的节点唯一性等。
9.RocketMQ调优
9.1 JVM层面优化
- 关闭偏向锁。
- 垃圾收集器改为G1
9.2 OS层面优化
- 修改交换分区的大小。
- 调大网卡的Ring Buffer大小,避免流量高场景的丢包问题。
- 中断聚合。
- 网卡队列CPU多核绑定。
- pageCache等优化
- 磁盘优化