利用事务消息实现分布式事务

    举一个电商的例子,用户在购物车中付款,会调用一个服务生成一条订单,并调用另一个服务将该商品从购物车中删除。这两个操作应该是原子性的,要么都成功,要么都失败,这就是事务要解决的问题。我们下面来谈谈事务的概念、分布式事务的复杂点和实现方式。

    一. 事务

    事务的四大特性ACID:原子性、一致性、隔离性、持久性

    1. 原子性

        原子性指一个事务的操作是不可分割的,要么成功要么失败,不会存在成功一半的情况。

    2. 一致性

        一致性指一个事务在执行完成的时间点前读取到的一定是更新前的状态,执行完成后读取到的一定是更新后的状态,不会存在一个时间点读取到执行到一半的状态。

    3. 隔离性

        隔离性指一个事务的执行不会被其他事务干扰。

    4. 持久性

        持久性指一个事务一旦提交,后续的操作和故障不会对事务的结果产生影响。

    二. 分布式事务

    在分布式系统中,有着不同的服务,不同的数据存储位置。在保证可用性和较好的性能的同时,实现数据的一致性是一件较为困难的事情。我们一般采用的都是替代解决方案,比如顺序一致性、最终一致性等等。

    在实际应用中,比较常见的分布式事务实现方式有2PC(二阶段提交)、TCC和事务消息。我们着重介绍一下事务消息实现的分布式事务。

    1. 事务消息的应用场景

        事务消息适用场景主要是那些需要异步更新数据,并且对数据的实时性要求的高的场景。比如,我们在开始提到的例子,创建订单后,购物车的数据延迟1秒清空也是完全可以接受的,只要最终购物车的数据和订单数据保持一直就可以了。

    2. 事务消息是如何实现的?

        事务消息需要消息队列提供相应的功能才能实现,RocketMQ和kafka都提供了事务相关的功能。

        回到刚才的例子,看看事务消息是如何在订单创建中完成工作的。

        首先,订单系统在消息队列上开启一个事务,然后订单系统会给消息队列服务器发送一个"半消息",这个半消息不是说消息内容不完整,而是说事务提交前,这个消息对消费者来说是不可见的。半消息发送成功以后,订单系统开始创建订单,并根据订单创建是否成功来决定事务消息是提交还是回滚。事务消息提交后,消费者就能获取到这个消息并作出相应的处理。这样一来就基本实现了"要么都成功,要么都失败"的一致性要求。

        其中还有一个麻烦的问题,如果在提交事务时遇到网络或者服务器故障导致提交失败了怎么办?RocketMQ和kafka都给出了各自的解决方案。kafka直接抛出异常,让用户自行处理。而RocketMQ增加了事务反查机制,如果RocketMQ没有收到提交或者回滚的请求,Broker会定期去Producer上反查这个事务对应的本地事务的状态,然后在根据反查结果决定提交或者回滚消息事务。为了支撑这个反查机制,我们的业务代码还需要实现一个反查本地事务的接口,告知RocketMQ本地事务是成功还是失败。

        在我们的这个例子中,反查本地事务的接口逻辑很简单,我们只要根据消息队列中的订单ID,在订单中查询这个订单是否存在即可,RocketMQ会自动根据反查结果提交或者回滚事务消息。

        这个反查事务的实现,并不依赖消息的发送方,也就是订单服务的某个实例节点上的任何数据。这种情况下,即使是发送事务消息的那个订单节点宕机了,RocketMQ依然可以通过其他订单节点来执行反查,确保事务的完整性。

猜你喜欢

转载自www.cnblogs.com/wang-yi-shan/p/11657831.html