RocketMQ第6集 消息的5大发送类型

一 消息的分类

1.1 普通消息

1.1.1 同步消息

同步发送消息是指,Producer发出⼀条消息后,会在收到MQ返回的ACK之后才发下⼀条消息。该方式的消息可靠性最高,但消息发送效率太低。

代码层面:

1.1.2 异步发送消息

异步发送消息是指,Producer发出消息后无需等待MQ返回ACK,直接发送下⼀条消息。该方式的消息靠性可以得到保障,消息发送效率也可以。

代码层面

RocketMQ支持异步消息发送,这意味着生产者在发送消息后不会等待消息的发送结果,而是通过回调函数来处理发送结果(如记录表记录失败消息,进行重发机制)。这种方式适用于对实时性要求不高,但需要高并发处理的场景。

综述就是虽然RocketMQ的异步发送允许生产者在发送消息后不等待结果,但通过异步回调和内置的重试及流控机制,生产者最终可以确保消息的成功发送。

1.1.3  单向发送消息

单向发送消息是指,Producer仅负责发送消息,不等待、不处理MQ的ACK。该发送方式时MQ也不返回ACK。该方式的消息发送效率最高,但消息可靠性较差。

代码层面:

1.2   顺序消息

发送消息的顺序和消费者消费消息的顺序要保持一致。

例如,现在有TOPIC ORDER_STATUS (订单状态),其下有4个Queue队列,该Topic中的不同消息用于描述当前订单的不同状态。假设订单有状态:未支付、已支付、发货中、发货成功、发货失败。

当发送和消费参与的Queue只有一个时所保证的有序是整个Topic中消息的顺序, 称为全局有序。

如果有多个Queue参与,其仅可保证在该Queue分区队列上的消息顺序,则称为分区有序。一般性的选择算法是,让选择key(或其hash值)与该Topic所包含的Queue的数量取模,其结果即为选择出的Queue的QueueId。

取模算法存在一个问题:不同选择key与Queue数量取模结果可能会是相同的,即不同选择key的消息可能会出现在相同的Queue,即同一个Consuemr可能会消费到不同选择key的消息。这个问题如何解决?一般性的作法是,从消息中获取到选择key,对其进行判断。若是当前Consumer需要消费的消息,则直接消费,否则,什么也不做。这种做法要求选择key要能够随着消息一起被 Consumer获取到。此时使用消息key作为选择key是比较好的做法。

1.3 延时消息*

典型的应用场景是,电商交易中超时未支付关闭订单的场景,12306平台订票超时未支付取消订票的场景。 当消息写入到Broker后,在指定的时长后才可被消费处理的消息,称为延时消息。

1.4 mq的事务场景

1.4.1 mq的事务消息场景

1场景描述:

事务消息场景:工行用户A向建行用户B转账1万元。可以使用同步消息来处理该需求场景

1.工行系统发送一个给B增款1万元的同步消息MBroker

2.消息被Broker成功接收后,向工行系统发送成功ACK

3.工行系统收到成功ACK从用户A中扣款1万元

4.建行系统从Broker中获取到消息M

5.建行系统消费消息M,即向用户B中增加1万元

2问题描述:

这其中是有问题的:若第3步中的扣款操作失败,但消息已经成功发送到了Broker对于MQ来说,只要消息写入成功,那么这个消息就可以被消费。此时建行系统中用户B增加了1万元。出现了数据不一致问题

3解决方案:

方案就是为了确保消息投递与扣款操作能够在一个事务中,要成功都成功,有一个失败,则全部回滚

1.4.2  XA事务解决方案

XA(Unix Transaction)是一种分布式事务解决方案,一种分布式事务处理模式,是基于XA协议的。XA模式中有三个重要组件:TC、TM、RM。

TC

Transaction Coordinator,事务协调者。维护全局和分支事务的状态,驱动全局事务提交或回滚。 RocketMQBroker充当着TC

TM

Transaction Manager,事务管理器。定义全局事务的范围:开始全局事务、提交或回滚全局事务。它实际是全局事务的发起者。 RocketMQ中事务消息的Producer充当着TM。

RM

Resource Manager,资源管理器。管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。 RocketMQ中事务消息的Producer及Broker均是RM。

XA模式是一个典型的2PC,其执行原理如下:

1.TM向TC发起指令,开启一个全局事务。

2.根据业务要求,各个RM会逐个向TC注册分支事务,然后TC会逐个向RM发出预执行指令。

3.各个RM在接收到指令后会在进行本地事务预执行。

4.RM将预执行结果Report给TC。当然,这个结果可能是成功,也可能是失败。

5.TC在接收到各个RM的Report后会将汇总结果上报给TM,根据汇总结果TM会向TC发出确认指令。

若所有结果都是成功响应,则向TC发送Global Commit指令。

只要有结果是失败响应,则向TC发送Global Rollback指令。

6.TC在接收到指令后再次向RM发送确认指令。

注意:

1)事务消息不支持延时消息

2)对于事务消息要做好幂等性检查,因为事务消息可能不止一次被消费(因为存在回滚后再提交的情况)

3)事务消息方案并不是一个典型的XA模式。因为XA模式中的分支事务是异步的,而事务消息方案中的消息预提交与预扣款操作间是同步的。 

1.4.3  基于RMQ事务消息实现最终一致性

1.半事务消息:发送方已经成功地将消息发送到了Broker,但是Broker未收到最终确认指令,此时该消息被标记成“暂不能投递”状态,即不能被消费者看到。处于该种状态下的消息即半事务消息

2.本地事务和事务消息一样,就是为了保证操作结果的一致性

3.普通消息和订单事务无法保证一致的原因,本质上是由于普通消息无法像单机数据库事务一样,具备提交、回滚和统一协调的能力。

而基于 RocketMQ 实现的分布式事务消息功能,在普通消息基础上,支持二阶段的提交能力将二阶段提交和本地事务绑定,实现全局提交结果的一致性RocketMQ事务消息使用了2PC+事后补偿机制保证了最终一致性。

流程如下:

1.生产者将消息发送至broker;

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

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

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

4.1 Commit: Broker 将半事务消息标记为可投递,并投递给消费者

4.2 Rollback: Broker 将回滚事务,不会将半事务消息投递给消费者。

5.在断网或者生产者应用重启的特殊情况下,若broker未收到发送者提交的二次确认结果,或者broker收到二次确认结果为unkown,未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一实例发起消息回查。

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

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

https://www.cnblogs.com/makemylife/p/18101509

1.4.4  半事务消息说明

1.生产端使用TransactionMQProducer发送业务消息到TopicA底层ransactionMQProducer填充Message的properties属性键值TRAN_MSG=true,表示该消息为事务消息

2.服务端Broker收到消息,存储时根据消息的properties的TRAN_MSG属性判断是否是事务消息。

如果为true(事务消息),把原Topic名字保存到Message的properties属性REAL_TOPIC中并把本消息的Topic替换成RMQ_SYS_TRANS_HALF_TOPIC(半事务消息topic)然后保存并返回结果(到此完成发送半事务消息)。

总结:发送端使用TransactionMQProducer进行发送消息,broker端进行判断是否是事务消息,如果是事务消息,将消息放到RMQ_SYS_TRANS_HALF_TOPIC(半事务消息topic)然后保存并返回结果

在rocket-broker依赖包中进行查看:

Broker收到消息,在做存储逻辑时根据消息的properties的TRAN_MSG属性判断是否是事务消息,如果true(事务消息),把原Topic名字保存到Message的properties属性REAL_TOPIC中,并把本消息的Topic替换成RMQ_SYS_TRANS_HALF_TOPIC,然后保存并返回结果(到此完成发送半事务消息)。 

原文链接:https://blog.csdn.net/WangMapleWang/article/details/139988233

1.5 批量消息*

生产者进行消息发送时可以一次发送多条消息,这可以大大提升Producer的发送效率。不过需要注意以下几点:

批量发送的消息必须具有相同的Topic

批量发送的消息必须具有相同的刷盘策略

批量发送的消息不能是延时消息与事务消息

默认情况下,一批发送的消息总大小不能超过4MB字节。如果想超出该值,有两种解决方案

方案1:

将批量消息进行拆分,拆分为若干不大于4M的消息集合分多次批量发送。

方案2:

1.Producer端需要在发送之前设置Producer的maxMessageSize属性

2.Broker端需要修改其加载的配置文件中的maxMessageSize属性

1.6 消息过滤

通过consumer的subscribe()方法指定要订阅消息的Tag。如果订阅多个Tag的消息,Tag间使用或运算符(双竖线||)连接。

consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");

通过SQL过滤, 可以实现对消息的复杂过滤只有使用PUSH模式的消费者才能使用SQL过滤。

二 消息的重试策略

2.1 生产端

Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,也称为消息重投机制。

只有普通消息具有发送重试机制,顺序消息是没有的

消息重投机制可以保证消息尽可能发送成功、不丢失,但可能会造成消息重复。消息重复在RocketMQ中是无法避免的问题

消息发送重试有三种策略可以选择:同步发送失败策略、异步发送失败策略、消息刷盘失败策略

2.2 消费端

对于顺序消息的消费,必要保证应用能够及时监控并处理消费失败的情况,避免消费被永久性阻塞。

注意,顺序消息没有发送失败重试机制,但具有消费失败重试机,无序消息的重试只对集群消费方式生效,广播消费方式不 提供失败重试特性。

对于无序消息集群消费下的重试消费,每条消息默认最多重试16次,

集群消费方式下,消息消费失败后若希望消费重试,则需要在消息监听器接口的实现中明确进行如下三 种方式之一的配置: 

方式1:返回ConsumeConcurrentlyStatus.RECONSUME_LATER(推荐) 方式2:返回Null 方式3:抛出异常

消费不重试配置方式:集群消费方式下,消息消费失败后若不希望消费重试,则在捕获到异常后同样也返回与消费成功后的相同的结果,ConsumeConcurrentlyStatus.CONSUME_SUCCESS,则不进行消费重试

 三 死信队列

3.1  死信队列

 死信队列是用于处理无法被正常消费的消息的,而其中的消息 则称为死信消息(Dead-Letter Message,DLM)。

死信队列中的消息不会再被消费者正常消费,即DLQ对于消费者是不可见的

死信存储有效期与正常消息相同,均为 3 天(commitlog文件的过期时间),3 天后会被自动删除

死信队列就是一个特殊的Topic,名称为%DLQ%consumerGroup@consumerGroup ,即每个消费者组都有一个死信队列

如果⼀个消费者组未产生死信消息,则不会为其创建相应的死信队列。

解决办法:

实际上,当⼀条消息进入死信队列,就意味着系统中某些地方出现了问题,从而导致消费者无法正常消费该消息,比如代码中原本就存在Bug。因此,对于死信消息,通常需要开发人员进行特殊处理。最关键的步骤是要排查可疑因素,解决代码中可能存在的Bug,然后再将原来的死信消息再次进行投递消费。

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值