dcddc

西米大人的博客

0%

系统学习rocketMq-基础篇

核心概念

nameServer

  • 用于服务发现,producer与consumer都会从nameServer获取Topic对应的broker,从而将消息发送至broker或者从broker获取消息,producer与consumer都会与nameServer保持长连接并通过心跳包告知状态

producer

  • 消息生产者,负责产生消息并发送消息到broker,根据topic从nameServer获取对应的broker信息,如果存在多个broker都持有相同topic做负载均衡的话,producer会轮询发送消息,而不是将消息全部发送至一个broker中

producer group

  • 同一组producer集合,称为producer集群

consumer

  • 消息消费者

consumer group

  • 同一组consumer集合,称为consumer集群,需要注意的是一个消费者集群中的订阅关系应该要保持一致,订阅关系一致指的是消费集群下所有Consumer实例订阅的消息Topic、Tag必须完全一致

broker

  • rocketmq的服务器,负责接收并存储producer发送来的消息,并会发送如存储消息的队列信息等数据到nameServer完成注册

topic

  • 消息的主题,通常表示消息的业务含义,用于区分不同消息
  • 同一topic下的消息,实际中可能负载均衡到不同broker存储,这称为topic分片

CommitLog

  • broker将消息写入文件CommitLog。消息进入broker后,按照顺序先写入commitLog(不进行长度和Topic的区分),文件命名为起始字节位置,默认大小为1G,写满后会自动产生新的数据文件。一旦落盘成功,消息便完成了持久化,不会丢失
  • 消息持久化分为同步刷盘异步刷盘(默认)两种方式,异步刷盘默认N秒强制刷盘一次,每M条强制刷盘一次,所以理论上某broker宕机瞬间历史的消息损失数量会在M条以内

MessageQueue

  • broker有多个MessageQueue存储消息的索引信息,包括在CommitLog上的offset、msgSize、tagHashCode。每个topic在broker上会有几个固定的MessageQueue,该topic下的每条消息的索引会随机存储到其中一个MessageQueue上,或者按Producer指定的分片规则路由存储到某个MessageQueue。Consumer按MessageQueue维度消费消息,任何时刻MessageQueue里的消息只能被同一个消费组下的某个Consumer实例消费

设计MessageQueue有两个作用:

  • 为了消费者的负载均衡。每个MessageQueue只会由一个consumer消费,如果MessageQueue数目大于consumerGroup的集群数,每个Consumer可能消费多个MessageQueue,反之,有的Consumer不会消费
    • 个人认为,不仅是负载均衡,还有两个原因:
      • 并行消费的思想,多个消费者可以并发消费一个broker的消息,提高吞吐量
      • 降低复杂度。如果多个consumer从同一个broker取该topic的消息,只有一个队列很难保证一条消息只会被一个consumer取走消费,极有可能出现重复消费,不易实现集群模式
    • rocketmq消费模式有集群模式广播模式。集群模式下每条消息只会被ConsumerGroup中的一个Consumer消费,一般也使用这种模式,上面说的“每个MessageQueue只会由一个consumer消费”也是针对这种模式。广播模式指的是消息会被集群中的每个Consumer消费,且不具备失败重投能力,一般不用
  • 实现顺序消费
    • 消息发布时,可以实现MessageQueueSelector,选择消息要投递到哪个MessageQueue。由于每个MessageQueue只会被一个Consumer消费,这就保证了消息会被顺序消费
      • 相比乱序消息,顺序消息丧失了消息发送和消息消费中的负载均衡策略,且一旦有消息消费失败,会阻塞后续消息,造成消息堆积

消息发布流程

对于乱序消息,Producer从nameserver获取要发布的Topic对应master broker地址列表,使用轮询的方式选择broker建立长连接,定时发送心跳。选择好broker发布消息时,因为broker中topic有多个MessageQueue,负载均衡策略是,将所有broker里面所有MessageQueue组成一个圈,按从头到尾循环遍历选择消息队列发送消息。

  • broker列表即存储该topic消息的broker集群
  • 对于顺序消息,Producer发送消息时会投递到指定的MessageQueue

消息消费流程

consumer从nameserver获取topic的broker集群列表,按均分的负载均衡策略选择topic的一些消费队列,然后从这些消费队列中获取消息索引,再到commitLog获取消息体

  • consumer已选择的消费队列不能同时被其他consumer消费
  • rocketmq同时支持推模式和拉模式的消息订阅方式

拉模式

拉模式就是短轮询,由消费者设定轮询频次,每次轮询请求新消息,broker直接将结果返回。适合于慢消费(生产速度大于消费速度)场景,每次都能拉取到新消息。但实际上,轮询的频率往往无法很好的适应消息生产的频率,频率太小将导致消息推挤在Broker中,频率太大的话增加系统负担、产生无谓的网络开销

推模式

推模式本质是长轮询,它能以较低的成本保证消息能被及时消费。实际开发中往往使用推模式
broker在没有数据时会暂存消费者的请求而不是返回结果。当有新消息或超过请求的hold时间,才发送结果给消费者
消费者通过requestId识别是哪次消息拉取请求,再执行回调方法消费消息,并发送下一次长轮询

消息重复投递

消息重复投递一直是消息中间件很难避免的问题,通常在网络异常时会发生消息重复投递的现象,例如Producer成功将消息投递到了服务端,而服务端返回的确认消息由于网络原因没有回传成功,则Producer认为发布消息失败会重新发布一样的消息导致重复;同样在broker向Consumer成功发送消息后,由于网络原因Consumer回传的消息没有发送到broker,使得broker认为消息投递失败,重投后导致了重复消息。

因此消费者需要对重复消息做幂等

rocketMq架构的简单示意图