![](https://img.haomeiwen.com/i2269232/5cce5b7d07dbe59e.png)
1. 简介
RocketMQ是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点。主要具有以下作用:
- 削峰填谷:解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题。例如,秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
- 系统解耦:解决不同重要程度、不同能力级别系统之间依赖导致一死全死。
例如,用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。库存系统订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
假如,在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。
- 异步处理:将不是必须的业务逻辑,进行异步处理,比如注册之后短信、邮箱的发送。
- 蓄流压测:线上有些链路不好压测,可以通过堆积一定量消息再放开来压测。
2. 相关角色
2.1 NameServer
NameServer的作用是注册中心,负责服务发现和路由,类似于Zookeeper。每个NameServer节点互相之间是独立的,没有任何信息交互。单台NameServer宕机不影响其他NameServer与集群,即使整个NameServer集群宕机,已经正常工作的Producer,Consumer,Broker仍然能正常工作,但新起的Producer、Consumer、Broker就无法工作。
2.2 Broker
Broker的作用是负责Topic消息存储、管理和分发等功能,Broker分为Master和Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master。Master和Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。
每个Broker与NameServer集群中的所有节点建立长连接,定时(每隔30s)注册Topic信息到所有的NameServer。NameServer定时(每隔10s)扫描所有存活的broker连接,如果NameServer超过2分钟没有收到心跳,则断开与broker的连接。
2.3 Producer
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Broker的路由信息,并向提供Topic服务的Master节点建立长连接,且定时向Master发送心跳。
Producer每隔30s(时间间隔由ClientConfig中的pollNameServerInterval配置决定)从NameServer获取所有Topic队列的最新数据,这意味着如果Broker不可用,Producer最多30s能够感知,在此期间内发往Broker的所有消息都会失败。
Producer每隔30s(时间间隔由ClientConfig中的heartbeatBrokerInterval配置决定)向所有关联的Broker发送心跳,Broker每隔10s扫描所有存活的连接,如果Broker在2分钟内每由收到心跳数据,则关闭与Producer的连接。
2.4 Consumer
Consumer与NameServer集群中的其中的一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Master和Slave节点建立长连接,且定时向Master和Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
Consumer每隔30s从NameServer获取Topic的最新队列情况,这意味着Broker不可用时,Consumer最多需要30s才能感知。
Consumer每隔30s(时间由ClientConfig中的heartbeatBrokerInterval配置决定)向所有关联的Broker发送心跳,Broker每隔10s扫描所有存活的连接,若某个连接2分钟内没有发送心跳数据,则关闭连接,并向该ConsumerGroup的Consumer发出通知,Group内的所有Consumer重新分配队列,然后继续消费。
当Consumer得到Master宕机通知后,转向Slave消费,Slave不能保证Master的消息100%都同步过来,因此会丢失少量的消息。但是一旦Master恢复,未同步过去的消息会被最终消费掉。
集群消费模式:消费该Topic中部分Queue中的消息。
广播消费模式:消费该Topic下所有Queue中的消息。
消费者端的负载均衡,就是集群消费模式下,同一个ID的所有消费者实例平均消费该Topic的所有队列。假如Topic有6个队列,某个消费者ID启了2个消费者实例,那么每个消费者负责消费3个队列。
3. 刷盘策略
RocketMQ的所有消息都是持久化的,先写入系统PageCache,然后执行刷盘操作,可以保证内存与磁盘都有一份数据,访问时直接从内存读取。
![](https://img.haomeiwen.com/i2269232/970661230c9a8e76.png)
- 同步刷盘(SYNC_FLUSH):消息写入MQ返回成功状态时,此时消息已经被写入了磁盘。(消息首先写入内存PageCache后,立即通知刷盘线程,刷盘完成后,返回消息写入成功的状态)
- 异步刷盘(ASYNC_FLUSH):消息写入MQ返回成功状态时,消息只是被写入内存PageCache,当内存里的消息积累到一定程度时,将会触发写磁盘操作。
4. 复制策略
- 同步复制(SYNC_MASTER):Master和Slave都写成功后返回成功状态。优点是如果Master出故障,Slave上有全部的备份数据,容易恢复。缺点是增大延迟,降低吞吐量。
- 异步复制(ASYNC_MASTER):只要Master写成功就返回成功状态。优点是低延迟,高吞吐。缺点是如果Master出故障,数据没有写入Slave,就会丢失消息。
推荐:异步刷盘+同步复制
5. 如何保证消息顺序
- 生产者:RocketMQ的Topic内的队列机制,可以保证存储满足FIFO(先进先出)。
- 消费者:在实际生产环境中,消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。
![](https://img.haomeiwen.com/i2269232/d8efb939cbac18ee.png)
例如,一个订单的顺序流程是:创建、付款、推送、完成。订单号做过取模运算再丢到selector中,selector保证同一个模的都会投递到同一条queue。即:相同订单号的 ---> 有相同的模 ---> 有相同的queue。
6. 如何解决重复消费问题
消费者端,可以采用日志表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么这条消息就不在处理,从而保证幂等性。
7. 事务消息
7.1 RocketMQ三种事务状态
- CommitTransaction:消息提交,当消息状态为CommitTransaction,表示允许消费者允许消费当前消息.
- RollbackTransaction:消息回滚,表示MQ服务端将会删除当前半消息,不允许消费者消费。
- Unknown:中间状态,表示MQ服务需要发起回查操作,检测当前发送方本地事务的执行状态。
7.2 RocketMQ如何实现事务消息
- 生产者发送Prepare消息到Broker(事务消息的发送为同步方式)。
- Broker收到消息后,将消息写到
Half Topic
(RMQ_SYS_TRANS_HALF_TOPIC
),写入成功后给生产者返回成功响应。 - 生产者获取到该消息的事务ID后,开始执行本地事务。
- 本地事务执行成功后提交Commit,失败则提交Rollback。Broker收到Commit或Rollback消息后,将消息写到
OP Topic
(RMQ_SYS_TRANS_OP_HALF_TOPIC
)该Topic存放着Prepare消息对应的Commit/Rollback消息,Broker利用Half Topic和 OP Topic 可以计算出需要回查的消息。如果是Commit消息,Broker会将消息存到真正的Topic里,从而消费者可以正常消费消息。如果是Rollback消息,Broker会删除Prepare消息而不进行下发,然后生产者根据业务要求决定是否进行其他的回滚操作。 - 如果本地事务执行超时提交或者提交Unknow状态则会触发broker的事务回查功能。
7.3 事务消息投递的三个Topic
- Half Topic:用于记录所有的Prepare消息。
- Op(Operation ) Half Topic:记录已经提交了状态的Prepare消息。
- Real Topic:事务消息真正的Topic,在Commit后会才会将消息写入该Topic,从而进行消息的投递。
![](https://img.haomeiwen.com/i2269232/9c40c6c2559c7f7c.png)
![](https://img.haomeiwen.com/i2269232/e5fcf6cb20116daf.png)
网友评论