最大努力通知与本地消息事务区别

发起通知者通过一定的最大努力机制通知接收者业务处理的结果。具体而言:

通知消息可以重复发送。因为接收方可能无法接收到通知,所以应该有一些机制来重复通知消息。消息可以被检查。如果即使经过最大努力,接收方仍未收到通知,或者接收方已经消费了消息但希望再次消费,接收方应该被允许主动从发起通知者那里查询消息信息。

在本地消息表模型中,发起通知者确保消息被发送并传递到接收方。换句话说,消息的可靠性由通知方保证。

而在最大努力通知模型中,发起通知者尽其所能通知业务处理结果给接收者,但消息仍然可能未被接收。因此,接收方需要主动调用发起通知者的接口来查询业务处理的结果,这意味着通知的可靠性依赖于接收方。

最大努力通知模式在处理分布式事务时具有一些问题和限制,这些问题可能会影响系统的可靠性和一致性。以下是一些最大努力通知模式可能面临的问题

  1. 消息丢失: 由于最大努力通知不保证通知一定会被接收方收到,存在消息丢失的风险。即使通知发送了多次,也无法完全消除这种风险。
  2. 不确定性: 接收方无法确定通知的状态,因为通知可能被接收、未被接收、接收后被处理失败等情况。这可能导致数据的不一致性。
  3. 接收方延迟: 由于通知是异步的,接收方可能存在不同程度的延迟,这可能导致业务处理的不同步。
  4. 重复通知: 在通知过程中,由于网络等原因,可能会导致通知的重复发送,从而使接收方重复处理相同的事务。
  5. 补偿复杂性: 如果发生通知失败或接收方处理失败,需要设计复杂的补偿机制来纠正业务操作的影响,这可能增加系统的复杂性。
  6. 接收方主动查询: 为了保证通知的可靠性,接收方需要主动查询通知结果,这可能增加系统的负担和复杂性。
  7. 不适用于关键业务: 最大努力通知模式适用于一些不太关键的业务场景,但对于需要高度一致性和可靠性的关键业务,这种模式可能不合适。

最大努力通知模式虽然提供了一种灵活的处理分布式事务的方法,但由于其不确定性和局限性,可能不适用于所有场景,特别是对于要求高度一致性和可靠性的应用。在选择通知模式时,需要根据业务需求和系统要求综合考虑。

本地消息表

本地消息表的方案最初由ebay的工程师提出,核心思想是将分布式事务
拆分成本地事务进行处理。本地消息表实现最终一致性。

img

img

具体案例

image.png

  • 步骤1和2,系统收到用户下单请求,将订单业务数据写入订单表中,同时把该订单对应的消息数据写入本地消息表中,订单表与本地消息表为同一个数据库,更新订单和存储消息为同一个本地事务,数据库事务处理,要么都成功,要么都失败。
  • 步骤3、4、5,订单服务发送消息到消息队列,库存服务收到消息,进行库存业务操作,更新库存数据
  • 步骤6和7,返回业务处理结果,订单服务收到结果后,将本地消息表中的数数据设置完成状态或者删除数据。
  • 步骤8,另起定时任务,定时扫描本地消息表,看是否有未完成的任务,有则重试。

本地消息表优缺点

本地消息表实现了分布式事务的最终一致性,优缺点比较明显。
优点
1.实现逻辑简单,开发成本比较低
缺点
1.与业务场景绑定,高耦合,不可公用
2.本地消息表与业务数据表在同一个库,占用业务系统资源,量大可能会影响数据库性能

MQ消息事务

消息事务的原理是将两个事务「通过消息中间件进行异步解耦」,和上述的本地消息表有点类似,但是是通过消息中间件的机制去做的,其本质就是’
**将本地消息表封装到了消息中间件中’**。

执行流程

  • 发送prepare消息到消息中间件
  • 发送成功后,执行本地事务
    • 如果事务执行成功,则commit,消息中间件将消息下发至消费端
    • 如果事务执行失败,则回滚,消息中间件将这条prepare消息删除
  • 消费端接收到消息进行消费,如果消费失败,则不断重试

消息事务优缺点

优点
1.可用性高
2.吞吐高
3.实现简单
缺点
1.下游服务失败,缺少事务回滚能力

RocketMQ消息事务案例

image.png

消息发送

1 发送方将半事务消息发送至消息队列RocketMQ版服务端

2 消息队列RocketMQ版服务端将消息持久化成功之后,向发送方返回Ack确认消息已经发送成功,此时消息为半事务消息。

3 发送方开始执行本地事务逻辑。

4 发送方根据本地事务执行结果向服务端提交二次确认(Commit或是Rollback),服务端收到Commit状态则将半事务消息标记为可投递,订阅方最终将收到该消息;服务端收到Rollback状态则删除半事务消息,订阅方将不会接受该消息

消息回查

1 在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达服务端,经过固定时间后服务端将对消息发送方即生产者集群中任意一生产者实例发起消息回查。

2 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。

3 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行操作。

发送事务消息为什么必须要实现回查Check机制?

当步骤1中半事务消息发送完成,但本地事务返回状态为TransactionStatus.Unknow,或者应用退出导致本地事务未提交任何状态时,从Broker的角度看,这条半事务消息的状态是未知的。因此Broker会定期向消息发送方即消息生产者集群中的任意一生产者实例发起消息回查,要求发送方回查该Half状态消息,并上报其最终状态。

Check被回调时,业务逻辑都需要做些什么?

事务消息的Check方法里面,应该写一些检查事务一致性的逻辑。消息队列RocketMQ版发送事务消息时需要实现LocalTransactionChecker接口,用来处理Broker主动发起的本地事务状态回查请求,因此在事务消息的Check方法中,需要完成两件事情:
检查该半事务消息对应的本地事务的状态(committed or rollback)。
向Broker提交该半事务消息本地事务的状态。

如果本地事务(写db)执行成功但是消息发送还没成功这段时间内,db数据更改,如何回查接口判断本地事务状态?

业务侧可以有多种实现方式,常用方案是在一个本地事务中同时写两条记录到db,一条是业务数据,一条记录本地事务执行成功数据(包含事务id字段)。回查的时候会根据***
*事务id****字段查询本地事务是否执行成功。

__END__