分布式事务方案之全局事务管理

一、使用场景

随着spring cloud 的应用,许多微服务应用系统开始普及。目前市面上分布式事务管理的方案很多,二阶段提交,三阶段提交,阿里的seata,以及一些tcc补偿性事务方案。其中阶段提交、tcc都是具有代码入侵性的,如果分布式事务多的情况下,该方案使用起来业务复杂度以及性能稳定性都会大打折扣。seata是一个不错的选择。但是有一种场景,seata确无法使用。

场景:

目前随着国外制裁,国内兴起了国产化的浪潮。于是出现了许多新老系统的过度与替换。例如我现在有10个微服务系统,分别都使用了各自的数据库。同时不可能一次性所有系统都进行统一替换更新,需要一个一个系统的去替换更新。如果10个系统中可能是hibernate框架,更或者其他语言**.net系统,C# 系统等。那么seata在此场景下是无法使用的。那这种情况下我们无法通过分布式事务来做到多系统事务一致性
为了后面业务便于理解我们定义一种场景
A系统为订单系统,数据库为 db_a
B系统为库存系统,数据库为db_b
c系统为库存系统,数据库为db_c
当下订单以后需要对db_b 中某张表做数据新增,同时需要对db_c系统中的库存表做数据更新

在这里插入图片描述

//下单
public void payOrder(){
// B系统 调用
	serviceA.insertOrder();
// C系统 调用
	serviceB.updateOrder();
}

public class ServiceB{
	public void insertOrder(){
	}
}
public class ServiceC{
	public void updateOrder(){
	}
}

解决方案:

通过全局事务管理+多数据源切换来实现
在这里插入图片描述

问题1:你可能会说你这不就搞成单系统了吗?

答:是的,你可以在将所有serviceB,servceC的服务都封装到一个jar包中,在未来你开始替换重构服务B,C时,你可以直接引用并调用。将serviceA.insertOrder(),改为一个通过代理的实现,以后只需改变实现就可以无缝切换。

二、多数据源切换实现

这里只给与大家点播如何实现,具体源码可留言。

//下单
@changeDs("db_a")
public void payOrder(){
// B系统 调用
	serviceA.insertOrder();
// C系统 调用
	serviceB.updateOrder();
}

public class ServiceB{
	@changeDs("db_b")
	public void insertOrder(){
	}
}
public class ServiceC{
	@changeDs("db_c")
	public void updateOrder(){
	}
}

1、定义@changeDs注解

后面写一个实现 MethodInterceptor 的拦截器来拦截标注了该注解的方法。

2、全局配置dataSourceMap

读取yml中配置的多个数据源,并以db_a 为key写入dataSourceMap中

3、线程队列参数实现数据源的栈队列

ThreadLocal<Deque> 来实现栈,先进后出,后进新出。
在这里插入图片描述

在这里插入图片描述
获取到db-a时,则通过改key去dataSourceMap 获取对应的数据源。

4、数据源执行拦截

当B方法执行sql时,则做数据源切换,具体实现AbstractRoutingDataSource类。可以参考

https://zhuanlan.zhihu.com/p/696226314

三、全局事务实现

原理:

我们将进行编程式事务,事务的声明与回滚可以见如下简单代码

Connection conn = ...;
    
try {
    
    // 开启事务:关闭事务的自动提交
    conn.setAutoCommit(false);
    
    // 核心操作
    
    // 提交事务
    conn.commit();
    
}catch(Exception e){
    
    // 回滚事务
    conn.rollBack();
    
}finally{
    
    // 释放数据库连接
    conn.close();
    
}

可以不论你使用的是什么事务管理器,springboot使用的是PlatformTransactionManager事务管理器,他的实现其实也就是对所有的Connection 做统一管理。因此我们只需要把每一个执行的Connection 存起来,最后请求结束以后再做提交或者回滚即可。

1、将connection 交给自定义的事务管理器

通过 AbstractDataSource 实现方法 getconnection()是将 Connection交给自定义事务管理管理起来。(定义一个线程变量即可)

2、在方法执行完成以后做提交

根据上面线程变量中的Connection 全部做提交,或者全部做回滚

注:当然你也可以将undolog日志执行前的快照做保存,以便于突然系统突然崩溃最回滚。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值