1. 集群架构
网图
- 所有Broker在启动时,向Name Server集群注册。并通过心跳进行状态保持。
- Broker周期性向Name Server同步Topic配置信息。
- Slave Broker从Master同步或者异步(根据配置)拷贝数据。
- Producer启动时,从Name Server获取到所有的Master Broker地址,并进行连接。Producer不能向Slave Broker写入消息。
- Producer周期性的从Name Server获取最新的Topic的路由信息,并缓存至本地。发送消息时,通过Topic查找Broker地址,并发送消息。
- Consumer启动时,从Name Server获取到所有提供指定Topic服务的Broker地址。并与之进行连接,包括Slave节点。
- Consumer周期性的从Name Server获取最新的Topic的路由信息,并缓存至本地。接收消息时,通过Topic查找Broker,从路由到的Broker接收消息。
- 接收消息时,如果Master Broker掉线,则从Slave Broker收取。
2. 存储方式
在rocketmq中,所有消息都是顺序写到一个叫做CommitLog的结构中。CommitLog是持续增长的,对应存储目录下的多个文件,默认情况下每当存储文件达到1G,就会新建一个文件继续存储数据,而文件的名字就是以当前CommitLog的长度命名。存储目录举例:
上文为了便于理解,提到过每个Topic下有多个Queue,用来存储实际的消息。但是,实际的情况是Queue存储的其实是消息在CommitLog中的偏移量,而不是实际的消息内容。每次读取消息时,是从Queue中拿到偏移量,然后通过偏移量从CommitLog中读取消息的实际内容。存储目录举例,topic为log-monitor,下图中列出的为0-3号队列。
3. 消费者分配策略
如上图所示,消费者对消费队列的分配策略是:对所有Broker上的指定Topic的所有Queue进行平均分配。也就说,当Consumer的总数大于Queue的总数时,会有Consumer分配不到Queue,也就接收不到消息。所以当Consumer数量多时,可以适当增加每个Topic对应的Queue数量。
4. 事务消息
事物消息通过二阶段提交实现。比如下图中,将操作数据库和发送消息组合为一个事务,只有数据库操作成功,生产者才会提交消息。只有提交的消息,才能被消费者消费到。
5. 二阶段提交以及事务回查问题
在上节中提到的发送事务消息的时候,会存在一个问题。假设这样一个场景:准备消息发送完毕,执行数据库操作成功,正准备发送commit消息时,程序发成异常退出。这个时候,数据库已经发生变更,但是服务端没有收到对应的commit消息,也就是说消费端接收不到这条消息,所以导致消息和数据库的状态无法对应。如果客户端一直无法恢复,那么这条消息就会丢失。
基于以上的情况,rocketmq实现了事务回查机制来解决这个问题。服务端有一个回查服务,每隔1分钟扫描一次所有的准备消息,发现如果有未提交也未回滚的消息,就会向同一个Producer Group中的随机一个Producer发送回查消息,Producer接收到回查消息,执行查询,将结果发送至服务端,所以,只要有同一个Producer Group下的任一客户端存活,就会保证最终数据库操作和消息达成一致。
注意:在官方开源的版本中删除了此功能。如果需要此功能,请访问 https://gitee.com/kuangye/MyRocketMQ/commits/master
6. 顺序消息
rocketmq支持发送顺序消息,保证consumer可以按顺序接收。实现方式是将所有消息发送到同一个Broker的同一个Topic的同一个队列中,但是在这种情况下,其他broker以及queue都是空闲的,所以会非常影响性能,所以除特殊情况不推荐使用:
7. 负载均衡
Producer启动时,会与集群中的所有Broker建立长连接。发送消息时,消息依次轮询发送至每一个Broker。
8. 消费进度
在rocketmq中,每个Consumer Group的消费进度是互不影响的。也就是说,如果一个Topic中有100条消息。那么只要订阅了这个Topic的所有消费组都会收到100条消息。当然,在同一个组中的不同消费者,获取到的消息是不同的,他们会平分这100条消息。