rocketmq 一些基本概念和一些使用的方法

133 阅读11分钟

RocketMQ 是什么?

RocketMQ是由阿里捐赠给Apache的一款低延迟、高并发、高可用、高可靠的分布式消息中间件(开源的),RocketMQ既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性

RocketMQ 的一些核心概念

Topic

定义:消息传输和存储的顶层容器,用于标识同一类业务逻辑的消息。主题通过TopicName来做唯一标识和区分。

  • 定义数据的分类隔离:  在RocketMQ的方案设计中,建议将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和订阅隔离性。
  • 定义数据的身份和权限: RocketMQ的消息本身是匿名无身份的,同一分类的消息使用相同的主题来做身份识别和权限管理。

组成

  • 主题名称: 用户自己定义
  • 队列列表:队列作为主题的组成单元,是消息存储的实际容器,一个主题内包含一个或多个队列,消息实际存储在主题的各队列内

消息类型

  • Normal:普通消息消息本身无特殊语义,消息之间也没有任何关联。
  • FIFO:顺序消息通过消息分组MessageGroup标记一组特定消息的先后顺序,可以保证消息的投递顺序严格按照消息发送时的顺序(对于指定的一个Topic,所有消息根据Sharding Key进行区块分区。同一个分区内的消息按照严格的FIFO顺序进行发布和消费。Sharding Key是顺序消息中用来区分不同分区的关键字段,和普通消息的Message Key是完全不同的概念)
  • Delay:定时/延时消息通过指定延时时间控制消息生产后不要立即投递,而是在延时间隔后才对消费者可见
  • Transaction:事务消息支持分布式事务消息,支持应用数据库更新和消息调用的事务一致性保障。

模型关系图

image.png

Transaction:

传统的场景:

image.png

在我们创建订单并付款的时候发生了什么,创建订单-> 付钱-> 更新订单/更新物流/更新积分/更新购物车的状态

  • 在传统的情况下我们需要增加加一个大的事务,最大的缺点是多分支环境下资源锁定范围大,并发度低,随着下游分支的增加,系统性能会越来越差。

image.png

我们基与普通的消息进行拆分,会存在什么问题呢?

  • 消息发送成功,订单没有执行成功,需要回滚整个事务。
  • 订单执行成功,消息没有发送成功,需要额外补偿才能发现不一致

在普通消息基础上,支持二阶段的提交能力。将二阶段提交和本地事务绑定,实现全局提交结果的一致性。 image.png

分步式事务流程图

image.png

  1. 生产者将消息发送至Apache RocketMQ服务端。

  2. Apache RocketMQ服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。

  3. 生产者开始执行本地事务逻辑。

  4. 生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:

    • 二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
    • 二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。
  5. 在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。服务端回查的间隔时间和最大回查次数(半事务消息因系统重启或异常情况导致没有提交,生产者客户端会按照事务异常检查间隔时间进行回查,若超过半事务消息超时时长后没有返回结果,半事务消息将会被强制回滚)

  6. 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。

  7. 生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。

消息

定义:消息是rocketmq中的最小数据传输单元。生产者将业务数据的负载和拓展属性包装成消息发送到 A服务端,服务端按照相关语义将消息投递到消费端进行消费。 消息具有以下特点:

  • 消息不可变性 消息本质上是已经产生并确定的事件,一旦产生后,消息的内容不会发生改变。即使经过传输链路的控制也不会发生变化,消费端获取的消息都是只读消息视图。
  • 消息持久化 默认对消息进行持久化,即将接收到的消息存储到服务端的存储文件中,保证消息的可回溯性和系统故障场景下的可恢复性

消费进度原理

消息是按到达服务端的先后顺序存储在指定主题的多个队列中,每条消息在队列中都有一个唯一的坐标,这个坐标被定义为消息位点.

image.png 每个主题的队列都可以被多个消费者分组订阅。若某条消息被某个消费者消费后直接被删除,则其他订阅了该主题的消费者将无法消费该消息。每条消息被某个消费者消费完成后不会立即在队列中删除,Apache RocketMQ 会基于每个消费者分组维护一份消费记录,该记录指定消费者分组消费某一个队列时,消费过的最新一条消息的位点,即消费位点。当消费者客户端离线,又再次重新上线时,会严格按照服务端保存的消费进度继续处理消息。如果服务端保存的历史位点信息已过期被删除,此时消费位点向前移动至服务端存储的最小位点。

image.png

重置消费位点

  • 初始消费位点不符合需求:因初始消费位点为当前队列的最大消息位点,即客户端会直接从最新消息开始消费。若业务上线时需要消费部分历史消息,您可以通过重置消费位点功能消费到指定时刻前的消息。
  • 消费堆积快速清理:当下游消费系统性能不足或消费速度小于生产速度时,会产生大量堆积消息。若这部分堆积消息可以丢弃,您可以通过重置消费位点快速将消费位点更新到指定位置,绕过这部分堆积的消息,减少下游处理压力。
  • 业务回溯,纠正处理:由于业务消费逻辑出现异常,消息被错误处理。若您希望重新消费这些已被处理的消息,可以通过重置消费位点快速将消费位点更新到历史指定位置,实现消费回溯。

消费重试

消费者出现异常,消费某条消息失败时,rocketmq会根据消费重试策略重新投递该消息进行故障恢复。 要想了解消息的重试机制,我们先了解下消费者的定义,因为不同消费者的类型事不一样的。

image.png

1.PushConsumer 是一种近乎全托管的消费者,这里的托管的含义在于用户本身并不需要关心消息的接收,而只需要关注消息的消费过程,除此之外的所有逻辑都在 Push 消费者的实现中封装掉了,用户只需要根据每条收到的消息返回不同的消费结果即可,因此也是最为普适的消费者类型。

image.png 严格禁止消息还未处理完成,就提前返回消费成功结果。此时如果消息消费失败,Apache RocketMQ 服务端是无法感知的,因此不会进行消费重试。

PushConsumer的重试

image.png

  • Ready:已就绪状态。消息在云消息队列 RocketMQ 版服务端已就绪,可以被消费者消费。
  • Inflight:处理中状态。消息被消费者客户端获取,处于消费中还未返回消费结果的状态。
  • WaitingRetry:待重试状态,PushConsumer独有的状态。当消费者消息处理失败或消费超时,会触发消费重试逻辑判断。如果当前重试次数未达到最大次数,则该消息变为待重试状态,经过重试间隔后,消息将重新变为已就绪状态可被重新消费。多次重试之间,可通过重试间隔进行延长,防止无效高频的失败。
  • Commit:提交状态。消费成功的状态,消费者返回成功响应即可结束消息的状态机。
  • DLQ:死信状态。消费逻辑的最终兜底机制,消息重试失败且超过最大重试次数,若保存死信消息功能开启,该失败消息会被投递至死信Topic。您可以通过消费死信Topic的消息进行业务恢复。
  • Discard:丢弃。消息重试失败且超过最大重试次数,若保存死信消息功能未开启,该失败消息会被直接丢弃。

image.png

实际消息两次消费的间隔时间=消费耗时+重试间隔+已就绪的持续时间

image.png 注:

2. SimpleConsumer 中则暴露了更多的细节给使用者。在,用户将自行控制消息的接收与处理。

image.png

  • Ready:已就绪状态。消息在Apache RocketMQ服务端已就绪,可以被消费者消费。
  • Inflight:处理中状态。消息被消费者客户端获取,处于消费中还未返回消费结果的状态。
  • Commit:提交状态。消费成功的状态,消费者返回成功响应即可结束消息的状态机。
  • DLQ:死信状态。消费逻辑的最终兜底机制,若消息一直处理失败并不断进行重试,直到超过最大重试次数还未成功,此时消息不会再重试,会被投递至死信队列。您可以通过消费死信队列的消息进行业务恢复。

image.png 消息重试间隔=不可见时间-消息实际处理时长

顺序消息

顺序消息是 Apache RocketMQ 提供的一种高级消息类型,支持消费者按照发送消息的先后顺序获取消息,从而实现业务场景中的顺序处理。 相比其他类型消息,顺序消息在发送、存储和投递的处理过程中,更多强调多条消息间的先后顺序关系。

如何保证消息的顺序性 消息的顺序性分为两部分,生产顺序性和消费顺序性。 如需保证消息生产的顺序性,则必须满足以下条件:

  • 单一生产者:消息生产的顺序性仅支持单一生产者,不同生产者分布在不同的系统,即使设置相同的消息组,不同生产者之间产生的消息也无法判定其先后顺序。
  • 串行发送:生产者客户端支持多线程安全访问,但如果生产者使用多线程并行发送,则不同线程间产生的消息将无法判定其先后顺序。 满足以上条件的生产者,将顺序消息发送至 服务端后,会保证设置了同一消息组的消息,按照发送顺序存储在同一队列中。服务端顺序存储逻辑如下:
  • 相同消息组的消息按照先后顺序被存储在同一个队列。
  • 不同消息组的消息可以混合在同一个队列中,且不保证连续。

image.png 消息组1和消息组4的消息混合存储在队列1中,消息组1中的消息G1-M1、G1-M2、G1-M3是按发送顺序存储,且消息组4的消息G4-M1、G4-M2也是按顺序存储,但消息组1和消息组4中的消息不涉及顺序关系。 如需保证消息消费的顺序性,则必须满足以下条件: 投递顺序

  • 消息按照服务端存储顺序投递,业务方消费消息时需要严格按照接收---处理---应答的语义处理消息,避免因异步处理导致消息乱序。
  • 即一条消息如果一直重试失败,超过最大重试次数后将不再重试,跳过这条消息消费,不会一直阻塞后续消息处理。(有限重试) blog.csdn.net/m0_38017860…