【橙子老哥】C# 实操分布式事务解决方案

hello,大家好,今天来到橙子老哥的分享时间,希望大家一起学习,一起进步。

欢迎加入.net意社区,第一时间了解我们的动态,文章第一时间分享至社区

社区官方地址:https://ccnetcore.com (上千.neter聚集地)

官方微信公众号:搜索 意.Net

添加橙子老哥微信加入官方微信群:chengzilaoge520

上一篇我们实操了高并发分布式缓存的解决方案, 这篇我们接着分布式的话题,使用c#去实操了一下分布式事务问题的解决方案

相信很多人已经对分布式事务这种面试八股文很熟悉了,说个七七八八不成问题,网上也有很多教程,但是多偏向于理论,没有实操,今天橙子老哥使用c#,带大家把整个流程落地一遍

希望下次遇到这个问题,能回想到橙子老哥的这篇文章,就是这篇文章的意义了

1、事务-ACID

长话短说,理论知识不能少:一个事务有四个基本特性,也就是我们常说的(ACID)。

  1. Atomicity(原子性) :事务是一个不可分割的整体,事务内所有操作要么全做成功,要么全失败。
  2. Consistency(一致性) :务执行前后,数据从一个状态到另一个状态必须是一致的(A向B转账,不能出现A扣了钱,B却没收到)。
  3. Isolation(隔离性):多个并发事务之间相互隔离,不能互相干扰。
  4. Durablity(持久性) :事务完成后,对数据库的更改是永久保存的,不能回滚。

以上这些特征相信大家在使用数据库的时候,已经了如指掌了,这里也不再过多赘述。

2、不处理分布式事务

通常的,如果是在单体架构中,为了保持数据的一致性,只需要在批量执行数据库操作的时候,开启事务,在最终完成操作的时候,再提交事务即可

但是如果各各操作是分布在不同的程序/数据库/服务器上,我们还按照原先的方式会怎么样呢?

废话少说,我们直接实操,准备代码:

这里我们模拟一个经典场景,订单服务库存服务,用户创建订单,订单数+1,库存数量-1,像这种场景,我们必须要确保数据的一致性,如果出现了订单加的多了,库存减的少了,那不就产生了超卖的严重生产事故?

//情况1,无分布式事务处理

//订单服务客户端
var orderServiceClient = new OrderServiceClient();

//库存服务客户端
var storeServiceClient = new StoreServiceClient();

//模拟执行10次下单
var i = 10;
while (i>0)
{
   
    try
    {
   
    	//入口,用户进行创建订单
        orderServiceClient.CreateOrder(storeServiceClient);
    }
    catch (Exception e)
    {
   
        Console.WriteLine(e.Message);
    }
    finally
    {
   	
    	//打印数据库订单和库存数量
        Console.WriteLine($"当前订单数量:{
     OrderServiceClient.Order},库存数量:{
     StoreServiceClient.Store}");
        i--;
    }

}
Console.WriteLine("完成");

class OrderServiceClient
{
   
    public static int Order {
    get; private set; } = 10;

    private Action? _tran;

    //新增订单到数据库(不会真正执行,返回委托,事务预处理,执行委托就是提交事务)
    Action AddOrderToDb()
    {
   
        return () => Order += 1;
    }
	
	//业务代码
    public void CreateOrder(StoreServiceClient storeClient)
    {
   
        //订单服务开启事务
        _tran = AddOrderToDb();
        storeClient.UpdateStore();
        //订单提交事务
        _tran.Invoke();

    }
}

//同理
class StoreServiceClient
{
   
    public static int Store {
    get; private set; } = 20;

    private Action? _tran;

    //新增订单
    Action DecreaseStoreToDb()
    {
   
        return () => Store -= 1;
    }

    public void UpdateStore()
    {
   
        //库存服务开启事务
        _tran = DecreaseStoreToDb();
        _tran.Invoke();
    }
}

在上面的例子中,我们在订单服务,调用了自己的数据库,同时又远程调用了库存服务,双方各自执行事务操作

当没有一方出现错误、网络完美、服务器稳定、内存够用,好像怎么执行也不会有任何问题

CAP:我又来了,分布式中要满足分区容错,一致性和可用性就不能同时抓

上面执行中,很明显有个地方容易出问题,如果在库存服务事务已经提交,返回的时候,网络波动订单服务没有收到结果,订单报错了,取消事务,库存执行完了,新增库存,导致数据不一致


	//业务代码
    public void CreateOrder(StoreServiceClient storeClient)
    {
   
        //订单服务开启事务
        _tran = AddOrderToDb();
        storeClient.UpdateStore();
        //情况1
         throw new Exception("订单服务网络波动,无法收到库存服务的回应,或者收到回应,但是事务没有提交宕机");
        
使用方法: 先附加DB_51aspx文件夹里面数据库到你的mssql 2000当中,用VS2005打开,修改web.config中的数据库配置: 这里改你的数据库用户名及密码 ,运行!OK! 后台路径为:admin/login.aspx 用户名 、密码为51aspx 采用多层分布式架构 -------WEB-------- 表示层,负责应用程序的表现形式、用户体验等。 -------Common------- 公共函数类,字符截取、验证用户输入信息等功能。一般被表示层调用。 -------BLL------ 处理应用程序的业务逻辑,被表示层调用。 -------DALFactory----- 抽象工厂,用于创建各种数据对象的方法,这里有配置文件和反射的运用。 -------Model------- Model程序集,存放体类,用于数据访问层和逻辑层调用 -------IDAL-------- 数据作接口,数据访问层现其接口并重写它(体现了面向接口的编程思想)。 -------Sqlserver---- 数据访问层,现具体的select、update、delete....作,重写IDAL接口。 -------DBUtility---- 数据访问类组件,这里使用的是SqlHelper 注:这是我参考一些多层架构的资料结合对多层的理解写的一个demo,用了两天时间,可能不是很标准,大家看的时候,有什么意见多多交流,一起探讨.分页那里还有点bug,不过我想以后再改进了(分页封装到数据层我总觉得不好),前台部分没有用控件(貌似一个高手告诉我,如果哪天你做.net不用控件了,你就可以了,汗一下!我就索性试一试。^_^)。有什么不合理的地方,欢迎提出来,我们一起学习,一起交流。 作者:李平
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值