今天这篇博文的主要目的是分享一下我设计Saga的实现思路来抛砖引玉,其实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实现这套流程都做了哪些工作来让大家对分布式事务有一个清晰的认知。首先来讲一下为什么要实现一个分布式事务。当我们的应用通过业务拆解后以物理隔离的模式运行在不同的物理机/虚拟机/容器上并且我们的数据库也做了相应的隔离后,我们没有办法通过发布一个单一的原子的ACID事务来达到事务的一致性。单个数据库在运行事务时通过对应的机制(诸如主流的MVCC多版本并发控制)来确保ACID。但是在跨多个数据库的情况下,各家数据库厂商就只能通过二阶段提交的XA协议来实现分布式事务。这种方案确实在一定程度上降低了分布式事务的复杂性不过性能上却无法做到很好的平衡。而更加常见的办法是通过应用自身调用各自的数据库原子事务以链条的形式来完成分布式事务,而saga作为其中的一种方案已经在各大企业的业务场景中被广泛的实践过了。

  下面我们就看看一个比较典型电商下单的场景,当电商注册用户在电商平台完成内用金充值后可以直接在平台上下单购买商品。下单的流程如下:点击购物车下订单->预扣库存->预扣内用金->创建订单,流程如下:

  在理想情况下当我们下订单顺利时,每一个服务只需要包裹各自的事务(扣库存/扣余额/创建订单),通过调用链即可完成一个完整的订单创建业务。然而由于数据库存在物理隔离,很多时候我们需要面对这种情况:

  这个时候saga的作用就体现出来了,它可以通过事件订阅/发布的形式帮你完成1-8的自动化的调用或者补偿调用,你需要做的只是一个配置和把对应的订阅/补偿方法给注册到这个配置的主题上即可。接下来我们就讲讲saga是如何一步一步来实现这套逻辑的。

二、配置

  *下面的所有实现均以Dapr实现集成为例,即(Oxygen-Saga.PubSub.Dapr + Oxygen-Saga.Store.Dapr),但是整体集成逻辑和(Oxygen-Saga.PubSub.Rabbitmq + Oxygen-Saga.Store.Redis) 区别不大,只有部分队列和持久化实现的区别。

  首先我们需要一个管理所有分布式事务配置的管理包项目,这个包会作为一个通用的nuget or 项目被其他具体的业务服务继承。然后所有的Saga配置都应该在这个包中编写对应的配置文件。同时每个服务会引入这个包并且创建自己的SagaConfiguration配置文件用于向saga服务申明自己需要创建Saga服务

三、注册

  注册的过程主要分为三个部分,一个部分就是需要把之前Topic配置文件引入到本地并形成我们的Saga配置,一部分是引入我们需要的第三方的组件来支持整个saga运行起来,另一部分是创建对应的订阅服务代理来支持对事件的订阅和分发。

四、流转

  当整个注册工作完成后启动项目会按照上图所示进行相关的注册和构造代理的工作,接下来就是具体业务的流转过程,所有的订阅会被一个SagaSubscribe(由上一步的RegisterSagaHandler触发并注入ISagaEventHandler后创建所得)接受到,并通过该Hub完成对应的路由投递到具体的方法函数进行业务流转。整个Hub结构如下所示,可以看到其实Saga是在每个服务注册了一个订阅器,通过订阅器接收到其他业务发布的Saga事件后通过代理的方式路由到具体的业务实现上来实现流程代理的。

  那Saga又是怎么实现自动化的发布下一个事件或者根据代理调用目标函数抛出异常后自动进行补偿事件的呢,这就要看Saga的传递模型结构了,整个模型结构如下图,可以看到这里的Topic其实就是对应到的上图的TopicName,所以当Proxy收到事件解析出Topic后即可路由到具体的函数进行处理,当函数处理完毕后,Proxy即可根据链表当前所在的节点获取Next,如果Next不为空,则发送当前函数回调的Result封装成一个消息包投递到Next所属的服务,如果Next为空则说明整个流程结束,不再执行任何操作。

五、异常

  同样的由于不能确保所有的业务能够完完全全的处理完毕自己业务比如当库存不足时,余额不足时,这时候就需要业务端自行通过触发异常的方式回调上一步的补偿,这样代理就会尝试从链表中获取Previous,如果Previous不为空,则发送当前函数抛出异常(SagaException<T>)回调的Result封装成一个消息包投递到Previous所属的服务,如果Previous为空则说明整个补偿流程结束,不再执行任何操作。不过还有一种情况,就是当前目标函数触发的是非SagaException异常,这种情况下Saga没有办法获取到上一步补偿所需的数据,所以这个时候就只能交给人工处理,也就是在注册部分第三步RegisterSagaHandler可以注入一个异常处理程序,当发生异常Saga无法处理时我们会尝试向Func<IServiceProvider, ErrorModel, Task> errorHandle 函数投递ErrorModel,由客户端自行决定如何处理异常。

六、持久化

  所有的消息在流转过程中会被包装为一个SagaData类型持久化到Store中,并根据流转类型被赋予三个状态,分别是Processing,Done,Error。这部分数据会在Store中保存24小时。未来扩展中会提供相应的接口获取这部分数据用于框架扩展。

好了,整个Saga的设计思路就到此讲解完毕了。在设计中肯定还有很多不足和有缺陷的部分,希望小伙伴们留言评论。最后再次附上系列开源地址,欢迎star fork issues 一键三连

一、电商Demo地址

二、通讯框架地址

三、Saga框架地址

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

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

    首先感谢张队@geffzhang公众号转发了上一篇文章,希望广大.neter多多推广dapr,让云原生更快更好的在.net这片土地上落地生根. 目录:一.通过Dapr实现一个简单的基于.net的微服务 ...

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

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

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

    本来想在Dpar 1.0GA时发布这篇文章,由于其他事情耽搁了放到现在.时下微服务和云原生技术如何如荼,微软也不甘示弱的和阿里一起适时推出了Dapr(https://dapr.io/),园子里关于da ...

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

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

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

    状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...

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

    我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓"stateful applications"真正具体的一个实现(个人认为),上一章讲到有状态服务可能很 ...

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

    在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接 ...

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

    Dapr提供了一些开箱即用的分布式链路追踪解决方案,今天我们来讲一讲如何通过dapr的configuration来实现非侵入式链路追踪的 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系 ...

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

    Oauth2授权,熟悉微信开发的同学对这个东西应该不陌生吧.当我们的应用系统需要集成第三方授权时一般都会做oauth集成,今天就来看看在Dapr的语境下我们如何仅通过配置无需修改应用程序的方式让第三方 ...

随机推荐

  1. JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念)

    JavaScript的执行过程 前言 编写一段JavaScript代码,它是如何执行的呢?简单来说,JS引擎在执行JavaScript代码的过程中需要先解析再执行.那么在解析阶段JS引擎又会进行哪些操 ...

  2. R语言服务器程序 Rserve详解

    R语言服务器程序 Rserve详解 R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大. R语言作为统计学一门语言,一直在小众领域闪耀着光芒.直到 ...

  3. Android官方文档翻译 九 2.2Adding Action Buttons

    Adding Action Buttons 增加动作按钮 This lesson teaches you to 这节课教给你 Specify the Actions in XML 在XML中指定动作 ...

  4. 【记录一个问题】go get -u github.com/go-redis/redis出现错误" invalid character '.' after top-level value"

    安装某个库的时候依赖于redis库,总是出现这样的错误: go install go: github.com/go-redis/redis/v7@v7.2.0: parsing go.mod: mis ...

  5. VS IDE之xml过大报错

    语料处理时遇到这个错误 在命令行中输入 $vsWherePath = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\ ...

  6. 阐述JDBC操作数据库的步骤

    1. 加载驱动. Class.forName("oracle.jdbc.driver.OracleDriver"); (注意:加载驱动在JDBC 4.0中是可以省略的,自动从类路径 ...

  7. 搭建服务器之文件共享cifs,nfs,samba

    cifs: 微软系统中用于网上邻居共享的一个机制,在linux下也可以通过命令mount -t cifs .....来挂载共享的文件目录等. nfs: linux之间的共享文件方式,基于rpc ser ...

  8. 不难懂————Promise对象 + 详解

    1.Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了P ...

  9. 扩容新生代为什么能够提高GC的效率

    扩容新生代为什么能够提高GC的效率 该文章默认读者对JVM的基础有所了解 在学习JVM的时候,遇到了个人感觉比较有意思的问题,通过视频学习整理了一下. 先来上图: 大部分情况下,对象都会进入Eden区 ...

  10. 如何在pyqt中实现丝滑滚动字幕

    滚动字幕的视觉效果 网上有很多博客介绍了滚动字幕的实现方法,懂得都懂,大部是 Ctrl C + Ctrl V,效果还很差,最后还是得靠自己.主要思路就是通过定时器定时刷新+绘制两段完整的字符串来达到 ...