RocketMQ分为四个部分,分别是NameServer、Broker、Productor、Consumer。
NameServer作用就是注册中心,包含注册、路由和动态剔除的功能。
路由:生产者和消费者从NameServer通过负载均衡策略,获取空闲Broker的地址,然后进行访问。
注册:Broker启动后注册到NameServer当中。
动态剔除:Broker每30秒向NameServer发送心跳包,表示Broker正常工作,如果超过120秒Broker都还是没有发送心跳包,那么NameServer会认定该Broker失效了,从路由当中进行剔除。NameServer每10秒会检测一次上述内容。
最终一致性:NameServer集群中,为了提高整体的性能,舍去了复杂的主从同步机制,而是通过每个Broker都和所有NameServer进行长连接来保证NameServer的最终一致性(NameServer职责很少,每台机器的QPS很高,所以只需少量机器就能满足大并发的要求)。每个NameServer都会维护一个自己的路由表。(Productor可以配置多个NameServer的地址,分号分割)
Broker是实际存储消息的组件,包含负责存储消息的CommitLog、ConsumeQueue和IndexFile,负责消息刷盘的同步刷盘和异步刷盘机制,集群环境下有同步复制和异步复制的机制。
CommitLog
CommitLog存储具体的消息,为了利用好磁盘的性能顺序写入,不同topic都放在同一个文件当中。文件名即是文件在磁盘中的起始物理地址,ConsumeQueue中存储了消息的起始位置和消息大小,通过ConsumeQueue存储的消息地址能够快速找到具体消息。
ConsumeQueue
ConsumeQueue类似索引,同样顺序写入,存储了消息的物理地址。为了保证某一topic的快速查找,ConsumeQueue根据topic进行目录划分。
IndexFile
索引文件,使用创建时间戳命名,一般通过key或时间戳查找消息的时候,就访问该索引文件。
思考
为什么不把CommitLog和ConsumeQueue的职责合一,将CommitLog设计成根据Topic存储的呢?这里我想应该是考虑了性能问题,如果存储的时候要去区分消息属于哪个Topic,那么必然增加存储消息的时间消耗导致无法快速响应。
同步刷盘
broker接收到消息之后需要等到消息写入磁盘才返回发送成功。这里只写入CommitLog,CommitLog复制到ConsumeQueue是后续Broker的处理。
异步刷盘
broker接收到消息之后写入到内存页了,便立刻返回消息成功。异步刷盘有消息丢失的风险。
同步复制
同步复制是集群环境下,等master把消息复制到slave上之后再返回成功。
异步复制
异步复制是集群环境下,在master写入消息成功了就返回成功。
Productor
生产者关心的是如何发消息,主要包括了普通消息、顺序消息、延时消息、批量消息等。
普通消息的发送,需要指定消息的topic、tag和消息内容,可同步发送、异步发送、单向发送。
异步发送可以指定消息发送成功/失败的处理,单向发送不需要等待响应。
具体示例可以从下面链接去查看。
样例(Example) · Apache RocketMQ开发者指南
顺序消息是由生产者和消费者决定的,同一个队列(tag)下的消息才能保证顺序消费,比如我们要将某订单ID的数据按照一定顺序处理,那么就需要将同一订单ID的消息发送到指定tag里面。
延时消息可以让消费者延时指定时间,才允许拿到消息进行消费,如订单30分钟超时,就是通过延时消息完成的,消费者30分钟后拿到订单判断是否支付成功,如果未支付成功则取消订单。RocketMQ的延时消息实现原理是将延时消息存放到一个指定的延时Topic当中,当时间到达之后把这个消息从指定的Topic转移到业务的Topic里面,从而实现延时。
批量消息支持一次发送最大4MB的消息内容,如果数据量太大可以分批发送。
Consumer
RocketMq的消费模式有两种,集群模式和广播模式,由消费者指定消费模式。(引入了Group的概念)
集群模式下,同一消费者组的同一Topic下的消费,只能被成功消费一次。
广播模式下,同一消费者组在同一Topic下的消费,假如N个消费者,每个消费者都可消费一次,一共消费N次。
集群模式下也可以实现广播模式的功能,如使用不同的消费者组订阅同一个Topic,那么便可以实现每个消费者组都对该Topic的消息消费一次。
下面是来源: