本文为基于《浅入浅出》-RocketMQ的阅读理解,感谢原作者~
组成部分
- producer
- consumer
- name server
- Broker
NameServer:
节点之间是相互独立的没有任何信息交互,平时主要开销是在维持心跳和提供Topic-Broker的关系数据,
有一点需要注意的是,Broker在向NameServer发送心跳数据的时候,会带上自己负责的Topic信息,
所以当Topic过多(万级别),在网络比较差的时候会误以为心跳失败。
每个Broker在启动的时候会到NameServer注册,Producer在发送消息的时候也会根据Topic到NameServer获取到
Broker的路由信息,Consumer也会定时获取Topic的路由信息。
Producer:
Producer由用户进行分布式部署,消息由Producer通过各种负载均衡,发送到Broker集群,延时低,支持快速失败。
RocketMQ提供了三种方式发送消息:同步、异步和单向。
同步:在接收方接收到消息并发回响应之后,才继续发送下一个数据包。
异步:不用等待接收方发回响应,就可以继续发送。
单向消息发送:只负责发送消息不等待服务器回应并且没有回调函数触发,一般用于对可靠性要求不太高的情况例如日志记录。
Broker:
具体提供业务的服务器,单个Broker与所有的NameServer节点保持长连接以及心跳,并定时将Topic信息注册到NameServer,底层的通信和连接都是基于Netty的。
负责消息存储,以Topic为纬度支持轻量级的队列,单即可支撑上万队列规模,支持消息推拉模型。
Consumer;
也是由用户进行分布式部署,支持PUSH以及PULL两种消费模式,支持集群消费和广播消息,提供实时的消息订阅机制。
PULL:主动从服务器拉取消息,只要拉取到消息,用户应用就会启动消费过程,即主动消费。
PUSH:封装了消息的拉取、消费进度以及其他内部的维护工作,将消息到达时执行的回调接口留给用户应用程序来实现。被动消费,但从实现上看还是从消息服务器中拉取信息,不同的是首先要注册消费监听 器,当监听器触发后才开始消费消息。
消息领域模型
Message:
其实就是要传输的消息。
一条消息必须拥有一个主题,可以看作是邮件发送的地址。
可以拥有一个可选的标签(Tag)以及额外的键值对,他们可以用于设置一个业务Key并在Broker上查找此消息以便在开发期间查找问题。
Topic:
可以看作是消息的归类,是消息的第一级类型,比如电商的交易消息物流消息…
与生产者消费者的关系很松散,一个Topic可以有0~N个生产者和消费者,一个生产者也可以同时向不同的Topic发送消息。
Tag:
可以看作是子标题,是第二级类型,用于为用户提供额外的灵活性,
Group:
分组,一个分组可以订阅多个Topic。分为ProducerGroup以及ConsumerGroup,一般来说同一个服务可以作为同一个Group,同一个Group一般来说发送和消费的消息是一样的。
Message Queue:
简称Queue或Q。消息物理管理单位。一个Topic将有若干个Q。若Topic同时创建在不通的Broker,则不同的broker上都有若干Q,消息将物理地存储落在不同Broker结点上,具有水平扩展的能力。
无论生产者还是消费者,实际的生产和消费都是针对Q级别。例如Producer发送消息的时候,会预先选择(默认轮询)好该Topic下面的某一条Q地发送;Consumer消费的时候也会负载均衡地分配若干个Q,只拉取对应Q的消息。 每一条message queue均对应一个文件,这个文件存储了实际消息的索引信息。并且即使文件被删除,也能通过实际纯粹的消息文件(commit log)恢复回来。
Offset:
在RocketMQ 中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用Offset 来访问,Offset 为 java long 类型,64 位。
consumer刚启动的时候会获取持久化的consumer offset,用以决定从哪里开始消费,consumer以此发起第一次请求。 每次消息消费成功后,这个offset在会先更新到内存,而后定时持久化。在集群消费模式下,会同步持久化到broker,而在广播模式下,则会持久化到本地文件。
消息消费模式:
消息消费模式有两种:Clustering(集群消费)和Broadcasting(广播消费)。
默认情况下就是集群消费,该模式下一个消费者集群共同消费一个主题的多个队列,一个队列只会被一个消费者消费,如果某个消费者挂掉,分组内其它消费者会接替挂掉的消费者继续消费。
而广播消费消息会发给消费者组中的每一个消费者进行消费。
Message Order:
Orderly(顺序消费)和Concurrently(并行消费)。
顺序消费表示消息消费的顺序同生产者为每个消息队列发送的顺序一致,所以如果正在处理全局顺序是强制性的场景,需要确保使用的主题只有一个消息队列。
并行消费不再保证消息顺序,消费的最大并行数量受每个消费者客户端指定的线程池限制。
完整的消费流程:
Producer 与 NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取Topic路由信息,并向提供 Topic 服务的Broker Master建立长连接,且定时向Broker发送心跳。即:有两个长连接。Producer只能将消息发送到 Broker master。但是Consumer则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。
NameService启动流程
- 第一步是初始化配置
- 创建NamesrvController实例,并开启两个定时任务:
- 每隔10s扫描一次Broker,移除处于不激活的Broker;
- 每隔10s打印一次KV配置。
Producer
通过轮训,Producer轮训某个Topic下面的所有队列实现发送方的负载均衡(可定制,比如取模决定到发送哪个queue)。
初始化流程很冗长,会根据配置创建很多线程池主要用来发送消息、拉取消息、查询消息、客户端管理和消费者管理,也有很多定时任务,同时也注册了很多请求处理器,用来发送拉取消息查询消息的。
面试:
幂等保证、
可用性保证:RocketMQ提供了两种持久化刷盘模式,同步和异步,两种底层都是使用的NIO实现,同步调用之后会等待结果,丢失一定的RT,但尽最大可能保证了可用性,异步相反,保证了RT,牺牲了一定的可用性。
消息发送顺序:对于相同topic的消息,比如说针对一个订单的创建、支付、发货、收货等顺序性链路,订单号相同,我们可以使用取模发MessageQueueSelector队列选择机制,这样就能保证该订单的顺序性消息发送到了同一个队列,而对于topic的同一个队列来说,rocketMQ保证了队列内消息的FIFO,剩下的只需要消费者顺序消费即可。
分布式事务:
半消息:是指暂不能被Consumer消费的消息。Producer 已经把消息成功发送到了 Broker 端,但此消息被标记为暂不能投递状态,处于该种状态下的消息称为半消息。需要 Producer
对消息的二次确认后(确认操作包括Commit以及Rollback),Consumer才能去消费它。 消息回查:
消息过滤:
Broker端消息过滤:在Broker中,按照Consumer的要求做过滤,优点是减少了对于Consumer无用消息的网络传输。缺点是增加了Broker的负担,实现相对复杂。
Consumer端消息过滤:这种过滤方式可由应用完全自定义实现,但是缺点是很多无用的消息要传输到Consumer端。
消息回溯:
Broker在向Consumer投递成功消息后,消息仍然需要保留。并且重新消费一般是按照时间维度。
例如由于Consumer系统故障,恢复后需要重新消费1小时前的数据,那么Broker要提供一种机制,可以按照时间维度来回退消费进度。
RocketMQ支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。