码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式


    在之前的系列文章中聊过分布式事务的一种实现方案,即通过在集群中暴露actor服务来实现分布式事务的本地原子化。但是actor服务本身有其特殊性,场景上并不通用。所以今天来讲讲分布式事务实现方案之saga模式,并在文后附上代码供各位读者参考,评论。

    目录:
    一、通过Dapr实现一个简单的基于.net的微服务电商系统

    二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

    三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    五、通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

    六、通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

    七、通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流

    八、通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪

    九、通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权 && 百度版Oauth2

    十、通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定

    十一、通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容

    十二、通过Dapr实现一个简单的基于.net的微服务电商系统(十二)——istio+dapr构建多运行时服务网格

    十三、通过Dapr实现一个简单的基于.net的微服务电商系统(十三)——istio+dapr构建多运行时服务网格之生产环境部署

    十四、通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧

    十五、通过Dapr实现一个简单的基于.net的微服务电商系统(十五)——集中式接口文档实现

    十六、通过Dapr实现一个简单的基于.net的微服务电商系统(十六)——dapr+sentinel中间件实现服务保护

    十七、通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载

    十八、通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

    十九、通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式

    二十、通过Dapr实现一个简单的基于.net的微服务电商系统(二十)——Saga框架实现思路分享

    附录:(如果你觉得对你有用,请给个star)
    一、电商Demo地址

    二、通讯框架地址

    三、Saga框架地址

    一、什么是Saga

            在领域驱动设计中,由于领域边界的存在,以往的分层设计中业务会按照其固有的领域知识被切分到不同的限界中,并且引入了领域事件这一概念来降低单个业务的复杂度,通过非耦合的事件驱动来完成复杂的业务。但是事件驱动带来了一些新的问题,由于以往一个原子性极强的逻辑被拆散到了一个一个小的领域中,原子性事务数据的强一致性无法被保证。为了解决这个问题,一般会采用事务补偿的方式来确保最终一致。

            当我们采用多个本地事务组合去进行业务处理时,由于业务其本身的复杂性,往往需要在多个事务中协调。而事件协调器(saga)就是一个专门降低其复杂度的设计,开发人员原则上只需要将事件和补偿按照一定顺序注册到协调处理器中,原则上协调器会按照注册的事件依次执行,若出现事件执行失败时,也会按照补偿列表进行相应的回滚。

            去年曾经在一篇文章里聊过Saga,链接在此。相比去年的那个小DEMO。我在此基础上基于Dapr的状态管理完成了另外一个开源项目Oxygen-Saga,地址:https://github.com/sd797994/Oxygen-Saga。当然这个项目既可以引入到dapr的环境中完成Saga事务,本身也可以独立的基于rabbitmq+redis来完成非Dapr环境下的Saga事务。今天主要讲解一下如何在Dapr环境下引入Saga来实现一个分布式事务。

      首先我们还是看看目前的一个订单下单逻辑,可以看到基于Actor服务,我们的请求是在订单服务里直连商品服务Actor完成库存减扣的,并通过Actor的周期性持久化机制完成事务落库,所以实际上是把分布式事务的复杂性通过Actor屏蔽掉了,并没有涉及到真正的分布式事务。

      从代码层面也可以看到,整个事务其实是在同一个方法里以同步调用Actor的方式完成的。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #region 私有远程服务包装器方法
    async Task<List<OrderGoodsSnapshot>> GetGoodsListByIds(IEnumerable<Guid> input)
    {
        return (await goodsQueryService.GetGoodsListByIds(new GetGoodsListByIdsDto(input))).GetData<List<OrderGoodsSnapshot>>();
    }
    async Task<bool> DeductionGoodsStock(CreateOrderDeductionGoodsStockDto input)
    {
        var data = input.CopyTo<CreateOrderDeductionGoodsStockDto, DeductionStockDto>();
        data.ActorId = input.GoodsId.ToString();
        return (await goodsActorService.DeductionGoodsStock(data)).GetData<bool>();
    }
    async Task<bool> UnDeductionGoodsStock(CreateOrderDeductionGoodsStockDto input)
    {
        var data = input.CopyTo<CreateOrderDeductionGoodsStockDto, DeductionStockDto>();
        data.ActorId = input.GoodsId.ToString();
        return (await goodsActorService.UnDeductionGoodsStock(data)).GetData<bool>();
    }
    #endregion
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public async Task<ApiResult> CreateOrder(OrderCreateDto input)
    {
        var mockUser = (await accountQueryService.GetMockAccount()).GetData<CurrentUser>();
        //申明一个创建订单领域服务实例,将远程rpc调用作为匿名函数传递进去
        var createOrderService = new CreateOrderService(GetGoodsListByIds, DeductionGoodsStock, UnDeductionGoodsStock);
        return await ApiResult.Ok("订单创建成功!").RunAsync(async () =>
        {
            var order = await createOrderService.CreateOrder(mockUser.Id, mockUser.UserName, mockUser.Address, mockUser.Tel, input.Items.CopyTo<OrderCreateDto.OrderCreateItemDto, OrderItem>().ToList());//通过订单服务创建订单
            repository.Add(order);
            if (await new CheckOrderCanCreateSpecification(repository).IsSatisfiedBy(order))
                await unitofWork.CommitAsync();
            await eventBus.SendEvent(EventTopicDictionary.Order.CreateOrderSucc, new OperateOrderSuccessEvent(order, mockUser.UserName));//发送订单创建成功事件
        },
        //失败回滚
        createOrderService.UnCreateOrder);
    }

      而在Saga方案里就变得不一样了,我们需要借助Saga的流程管理器在多个实例中流转我们的事务,通过事件订阅和发布来最终完成一个分布式事务。具体的调用流程如下:

    可以看到整个流程完全依赖于SagaManger来提供对应的调用策略,而作为开发人员只需要为每个策略提供对应的委托函数,对应的具体流程如下:

      一、首先是订单服务和商品服务在系统初始化时需要注册对应的配置文件到本地Saga管理器,并且需要提供对应的流程处理委托(包括业务事件委托+补偿事件委托+异常处理委托)。

      二、在客户发起一个订单时,只需要创建一个Saga流程实例,剩下的就交给Saga,它会自动帮我们流转整个业务逻辑而无需人工插手。

    二、talk is cheap, show me the code

    首先我们需要为整个订单创建流程设计一组Topic:

    接着我们创建一个Saga配置注册这组topic进去,流程比较简单,第一步是扣除库存,下一步是创建订单,补偿事件只有一个就是库存回滚:

    再然后我们需要创建这些Topic对应的事件处理器:

    订单服务:

    商品服务: 

    接着我们在各自的服务里去实现它们:

    商品服务:

    订单服务:

    然后我们在订单用例里创建一个Action用于启动saga流程(注意是第二个方法):

     最后我们在商品和订单服务中引入saga组件并注册事件和异常回调委托(注册代码相似,此处仅展示其中一个服务的):

    接着我们修改m站的创建订单接口,修改为新的Saga流程接口,然后编译整个项目,启动并测试一下:

     

     

     可以看到整个流程被顺利的通过Saga管理器流转完毕。接着我们尝试注入一个故障,看看能否正常的被异常订阅器捕获到:

     编译后再测试一下,我们可以看到事件异常订阅器里已经成功捕获到了这个异常:

     接着我们在创建订单注入一个异常,让商品库存进行回滚操作,这里由于下单后异常商品补偿所以在界面上是体现不出来的(当然在真实的业务场景中一般是下单5秒等待后查询订单创建情况,或发送一条站内信告知下单失败),这里我们通过打印控制台来演示:

    接着我们运行并下单,追踪商品服务的日志:

    可以看到由于订单创建失败,saga触发了补偿事件并成功执行了补偿。好了,关于saga的演示就到这里,可尝试拉取最新的商城源码执行即可看到效果。

  • 相关阅读:
    java项目-第144期ssm农产品供销服务系统-java毕业设计_计算机毕业设计
    【区域生长】代码
    Ubuntu工具-2 OBS Studio
    基于新版Opencv5.x(C++)流媒体视频流实现网页浏览器人脸检测
    【论文|复现]Vertebra-Focused Landmark Detection For Scoliosis Assessment
    SpringBoot之使用Redis和注解实现接口幂等性
    PostGIS简单使用
    JS-数组
    vs2019快捷键注释不起作用
    CYEZ 模拟赛 4
  • 原文地址:https://www.cnblogs.com/gmmy/p/15917585.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号