对于rocketMq客户端来说,无非就两个任务,消息生产与消息消费
对于生产者来说,需要知道消息可以往哪里发送
对于消费者来说,需要知道可以从哪里拉取消息
本文会从客户端与namesrv、broker的交互的角度讲述消息生产与消息消费大致原理,当然,重点还是围绕上文中提出的queue进行讲述
客户端包括生产者与消费者,它们有共同的交互逻辑,也有不一样的交互逻辑:
- 无论是生产者还是消费者,在启动时都需要指定一个或多个namesrv地址
- 客户端启动后,会以30秒一次的频率执行心跳注册以及路由更新动作
心跳注册:向所有master broker发送心跳信息(broker的信息来源在下面的路由更新中会介绍),包括消费订阅信息、生产者信息,broker在收到心跳信息后,会将信息提取并保存到内存中
路由更新:
- 从namesrv列表中随机选取一个,与之建立连接(并将连接保持,后续以此连接为准,直到连接不可用,才会换成另一个namesrv进行连接)
- 从建立连接的namesrv上拉取所有topic的路由信息,主要包括broker的addr,topic对应的queue信息(也就是在前面一篇"nameserver与broker的交互"中提到的路由信息),会根据返回的queueData中的写队列数(writeNums)创建对应数量的queue,每个queue对应一个queueId,与broker端的queue对应
- 如此,在客户端的内存中,就可以查到所有broker的连接地址,并且可以根据topic查询到其对应的所有queue{brokerName,queueId}信息了,生产者会以此为根据进行消息的发送,消费者以此为根据进行消息消费
注意,由于路由更新动作定时执行,所以一旦有broker数量变动(新增机器,上线下线),在下一次执行该动作时,内存中的queue列表就会对应更新,那queue列表到底是什么用意?
为什么说queue是消息生产时broker权重分配,消息消费能力横向扩展以及broker横向扩容的关键呢?
假设,当前broker集群中有两台master broker,broker1和broker2,其中broker1的可写队列数为2,broker2的可写队列数为3,在控制台创建了一个topicA,现在在客户端第一次拉取路由信息后,客户端的内存中对于topicA,会有5个queue,分别是{broker1,queueId0},{broker1,queueId1},{broker2,queueId0},{broker2,queueId1},{broker2,queueId2},基于这5个queue,我们看看消息的生产与消费,以及broker横向扩容
producer生产消息:
- 消息发送时,会默认以轮询queue列表的方式,往对应broker上对应queue中发送消息。在本例中,如果生产者S发送了五条topic为topicA的消息,其中会有两条消息进入broker1,三条消息进入broker2,broker1与broker2的权重比为2:3。写队列数可以在broker的配置文件中进行配置,实际生产中我们可以根据机器配置来决定该配置项的比重。
- 如果消息生产速度过快,导致broker负载过高,可以向现有的broker集群中增加master broker,consumeQueue列表会对应变化,比如新增的为broker3,写队列数配置为5,那此时consumeQueue的数量就增加了5个,新的权重比就是2:3:5,broekr1和broker2的压力就降低了一半左右,实现了broker的横向扩容
consumer消费消息:在介绍consumer前,需要搞清楚这几个概念
- group:消费者启动时,需要指定一个groupId,使用相同groupId的消费者属于同一个group。同一个group下的消费者消费逻辑必须完全一致(包括Tag的使用)。
- 集群消费:当使用集群消费模式时,每条消息只会被集群内的一个消费者处理。
- 广播消费:当使用广播消费模式时,每条消息都会被集群内的所有消费者处理
集群消费是更普遍的模式,举个简单场景,orderService服务发布了一条订单生成的消息,goodsService服务订阅该消息扣减库存,无论goodsService部署了多少台实例(实例数=消费者数),你肯定希望只扣减一次库存
下面就重点介绍集群消费模式的工作原理,consumer消费消息时,需要算出自己应该负责消费topic下哪些queue中的消息,会从任意一台broker上查询与自己处于同一个消费者组的所有消费者ID,然后按平均分配的策略算出自己应该负责的queue列表
平均分配是关键,当consumer的数量发生变化(新增机器,上线下线),broker会向所有consumer发起通知,消费者会重新计算自己负责的queue,以达到实时均衡。所以如果消费速度跟不上,新增机器就可以立即分摊一部分消息,实现消费能力的横向扩展
在算出自己负责的queue列表之后,还需要知道当前应该从queue的哪个位置开始消费(也就是offset),集群消费模式下由broker控制offset
broker在内存中维护offset值,并且,后台会有定时任务(5S一次),将offset刷入磁盘,磁盘内容为json格式,如下:topic_1@CID_1表示topic_1下的消费者组CID_1,0:24表示queueId为0的queue已消费到24的位置
消费者新加入消费时,从broker上查询最近offset开始消费,broker在处理消息拉取后,会返回下一个该消费的位置:nextoffset,消费者拿到返回信息后,会基于nextoffset发起下一次的拉取,如果nextoffset并没有变大,说明这个queue里当前没有可消费的消息。