半步多 玄玉的博客

RocketMQ原理02之生产消费与负载均衡

2022-01-02
玄玉

生产与消费

生产方式

producer有三种生产消息的方式

  • 同步(sync):消息投递出去之后,broker同步返回成功或失败
  • 异步(async):消息投递出去之后,不用等结果,自己写一个callback来接收投递结果
  • 单向(oneway):就是啥也不管,无脑投递

这里实际是要结合broker的刷盘方式,来看用哪种生产方式(多方配合)

消费方式

消费方式一般分为推和拉两种

  • PUSH:消息队列主动将消息推给消费者(优点是消息实时性高,缺点是忽略了客户端的消费能力)
  • PULL:消费者主动向消息队列拉取消息(缺点是消息实时性低,可能造成大量无效请求)

RocketMQ的消费方式介于推和拉之间,它使用了一种长轮询机制,来平衡推拉各自的缺点

  1. Consumer发送拉取消息请求
  2. Broker hold 住请求,直到有新消息再返回
  3. 请求超时(超时时间默认30s),Consumer再次发起请求

这样保证了实时性(一直有一个连接在),也没有过多的无效请求(30s才超时,没有数据时,一分钟才发俩请求)

而且客户端收到请求的回复之后,如果处理不过来,可以等数据处理完,再发起下一次拉取请求,不用立即发起

所以它是按照客户端的处理能力去尽量实时的拉取消息,兼顾了性能和实时时性

集群消费

  • 集群内部:单条消息只会被消费一次,且各节点会均匀消费topic消息
  • 多个集群:则各集群消费全量的消息,且单条消息在每个集群也只会被消费一次

也就是说,集群消费的模式,它要求一个队列只能被一个消费者消费(但一个消费者可以消费多个队列)

因为一个队列如果被多个消费者消费,那消费的offset该怎么移动,就是件麻烦事儿

比如有User集群和Order集群,都去消费同一个topic消息

那么单条消息既会被User集群消费到,也会被Order集群消费到

且在每个集群内都只会被消费一次(具体被集群里的哪个节点消费,则由负载均衡决定)

并且这俩集群都会消费到该topic的全量消息(各自集群内部根据节点数量均匀的消费)

可以把User集群和Order集群理解成两个group,即:组内竞争消费,组间广播消费

负载均衡

RocketMQ 中的负载均衡都是在 Client 端完成的

Producer端负载均衡

生产者端会定时获取到主题的队列信息,这样知道了topic在哪几个broker上都有哪些queue

于是,投递消息时,就可以通过本地算法,指定这个消息分发到具体broker上的具体queue上去

默认的负载均衡算法是采用随机递增取模(生成一个随机数,然后按队列数取模)的方式

并且producer端的容错机制是以故障延迟的方式实现的

即:当若某队列出问题导致发消息失败,那么再次取模到该队列时,会跳过并为其设置一个 N 毫秒的失效时间

等到下次再碰到该队列时,如果失效时间还没到,就继续跳过,要是时间到了就看还能不能往上面发消息

能发消息就用它,不能发消息就再为其置一个失效时间(比上一次的失效时间还要长一些)

Consumer端负载均衡

由于客户端本身并不知道彼此的存在,所以客户端独自很难实现均衡消费

而RocketMQ是通过Rebalance机制来实现的Consumer端负载均衡

它可以从broker收集到客户端数据(客户端会上报心跳),再加上定时触发的Rebalance(大概20s一次)

consumer就会得到同一个topic的所有队列信息和所有订阅了的消费者信息

再根据consumer的数量和队列数量,来平均分配(有点类似分页,你分几个,我分几个,大家平均着来)

最后,分配完,再和本地结果(也就是上一次结果)做一次对比

如果,上一次结果中分配的某个队列不在本次分配的队列列表里,那就剔除该队列

最终,就是要保证一个队列只会被一个消费者使用,不会出现一个队列被两个人消费的情况

不过,当consumer节点数超过topic队列的数量时,则必然会有至少一个节点分配不到队列而处于空闲

注意:每个队列的长度均匀,消息量均匀,这是由生产者做的


相关文章

Content