.NET core实现一个简易的事件协调器(saga)
在领域驱动设计中,由于领域边界的存在,以往的分层设计中业务会按照其固有的领域知识被切分到不同的限界中,并且引入了领域事件这一概念来降低单个业务的复杂度,通过非耦合的事件驱动来完成复杂的业务。但是事件驱动带来了一些新的问题,由于以往一个原子性极强的逻辑被拆散到了一个一个小的领域中,原子性事务数据的强一致性无法被保证。为了解决这个问题,一般会采用事务补偿的方式来确保最终一致。
事务补偿机制有多种实现方式,有基于数据库自带的基于2PC的XA协议、也有在逻辑层通过TCC实现,抑或采用多个本地事务组合的方式来实现。
当我们采用多个本地事务组合去进行业务处理时,由于业务其本身的复杂性,往往需要在多个事务中协调。而事件协调器(saga)就是一个专门降低其复杂度的设计,开发人员原则上只需要将事件和补偿按照一定顺序注册到协调处理器中,原则上协调器会按照注册的事件依次执行,若出现事件执行失败时,也会按照补偿列表进行相应的回滚。在微软其开源项目eshopcontainer中就提出了process manager概念。通过一个process manager来协调多个微服务之间的事务。
今天我们就通过一些简单的代码设计,来还原一个简易的基于事件总线的协调器。
废话不多说,先上代码:https://github.com/sd797994/EventCoordinator
解决方案包含两个项目(TargetFramework为.net5,如果你没有安装.net,可以改成netcoreapp3.1),一个是演示用的webapi demo。包含基本的控制器和一组事件及事件订阅处理服务。演示项目流程如下:
客户端访问接口下订单->发布订单创建事件->订单预创建订阅处理器创建订单编号->发布订单预创建成功事件->商品扣除订阅处理器订阅订单预创建成功事件并进行商品库存扣除->发布库存扣除成功事件->用户余额扣扣除订阅处理器订阅库存扣除成功事件并执行用户余额扣除->发布用户余额扣除成功事件->订单创建订阅处理器订阅用户余额扣除成功事件创建订单。若其中每一步处理失败,则依次进行回滚。
若一切正常,则流程结果执行如下:
模拟最后一步订单创建失败时,全部回滚的结果如下:
模拟订单创建失败用户金额回滚成功商品库存回滚异常时,结果如下(在真实的业务场景中出现此类情况应该进行系统预警人工处理,我这里采用logger.error模拟警戒级别):
下面我们来看看实现思路和代码,打开EventCoordinator项目,我们可以看到事件总线/事件协调器/通用三个文件夹。其中通用类是一些帮助方法不再赘述,事件也是基于System.Threading.Channels的简易异步事件总线实现,也不再赘述。主要说说协调器。
协调器的核心主要是EventProcessManager.cs以及EventProcessManagerPipline.cs以及ProcessConfigure.cs三个文件来实现的。我的设计逻辑如下:EventProcessManager管理所有的流程性事务。所以其包含长事务的注册和启动逻辑。而事务注册实际上就是一个构造委托代理的过程,我把它命名为ProcessConfigure,通过创建一个流程配置实例,将向EventProcessManager注册的委托作为“配置”的一部分创建其对应的方法委托,再在具体执行流程时从队列中取出配置并执行其的excute方法来发布事件,并且通过托管委托的方式在代理订阅器里执行真正的委托。而所有的入队、入栈我创建了一个EventProcessManagerPipline实例来保存我们的ProcessConfigure,这个管道包含一个队列和一个栈,由于事件是按照先进先出的方式执行,所以事件委托创建的配置会以队列的方式保存。而补偿则是按照先进后出的方式执行,所以补偿委托创建的配置会以栈的方式保存。而整个管道事务流转的核心均在EventProcessManagerPipline的Start方法中。
Start的核心逻辑如下:启动一个流程,在一个while循环中 从当前的队列中弹出一个事件配置。执行事件配置的send方法,并且通过AutoResetEvent的方式阻塞等待信号。当真正的业务委托执行完毕后会触发代理的AutoResetEvent.set(),如果业务执行callnext,则会将当前事件处理的结果作为callbackevent的一部分存储在缓存里,方便顺序回滚。如果执行callback,则直接执行回滚。如果所有的事件/补偿执行完毕,则流程执行完毕。
结语:整个代码其实比较简单,仅仅是我对长事务治理思想的一个粗浅理解,可能有误,恳请评论区大佬指正。。。
.NET core实现一个简易的事件协调器(saga)的更多相关文章
- 使用lua实现一个简单的事件派发器
设计一个简单的事件派发器,个人觉得最重要的一点就是如何保证事件派发过程中,添加或删除同类事件,不影响事件迭代顺序和结果,只要解决这一点,其它都好办. 为了使用pairs遍历函数,重写了pairs(lu ...
- 用pyqt5做一个简易的音乐播放器
需求 要求可以读取音频文档,有播放和暂停的功能 附上代码(1)UI界面 # -*- coding: utf-8 -*- # Form implementation generated from rea ...
- 用条件变量实现事件等待器的正确与错误做法--转自陈硕的Blog
用条件变量实现事件等待器的正确与错误做法 TL;DR 如果你能一眼看出 https://gist.github.com/chenshuo/6430925 中的那 8 个 Waiter classes ...
- Caliburn.Micro 杰的入门教程4,事件聚合器
Caliburn.Micro 杰的入门教程1(原创翻译)Caliburn.Micro 杰的入门教程2 ,了解Data Binding 和 Events(原创翻译)Caliburn.Micro 杰的入门 ...
- Server 2008 R2 事件查看器实现日志分析
在 windows server 2008 R2 中,可以通过点击 "开始" -> "管理工具" -> "事件查看器" ,来打开 ...
- .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...
- .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]
原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...
- 基于OpenGL编写一个简易的2D渲染框架-07 鼠标事件和键盘事件
这次为程序添加鼠标事件和键盘事件 当检测到鼠标事件和键盘事件的信息时,捕获其信息并将信息传送到需要信息的对象处理.为此,需要一个可以分派信息的对象,这个对象能够正确的把信息交到正确的对象. 实现思路: ...
- ENode 2.0 - 第一个真实案例剖析-一个简易论坛(Forum)
前言 经过不断的坚持和努力,ENode 2.0的第一个真实案例终于出来了.这个案例是一个简易的论坛,开发这个论坛的初衷是为了验证用ENode框架来开发一个真实项目的可行性.目前这个论坛在UI上是使用了 ...
随机推荐
- iOS二进制方案真实落地经验(30分钟降低到10分钟以内)
iOS二进制方案真实落地经验(30分钟降低到10分钟以内) 我们做iOS二进制化断断续续尝试了一年多了,来来回回换了三个架构师去尝试落地,今日完全落地,在此做个总结 背景 工程基于cocoapod的组 ...
- KMP算法解题模板(更新)
/* kmp算法的主要作用在于对next数组的运用,所以这里只给出next数组的模板 性质1:对于每一个长度len的子串,该子串的最小循环节为len-next[len] 性质2:kmp的next不断向 ...
- 论文解读DEC《Unsupervised Deep Embedding for Clustering Analysis》
Junyuan Xie, Ross B. Girshick, Ali Farhadi2015, ICML1243 Citations, 45 ReferencesCode:DownloadPaper: ...
- 《剑指offer》面试题60. n个骰子的点数
问题描述 把n个骰子扔在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所有可能的值出现的概率. 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i ...
- C#进阶——记一次USB HID的各种坑(x86,x64,win10,win7)
一.简叙 写工控上位机的搬砖人,难免会遇到USB通讯,在一个项目中,我写的上位机使用USB HID协议和STM32通讯传输数据,从零大概花了几天找例程,找资料,最后是各种搬砖修补,终于出来了一个出版D ...
- opencv 4.0 + linux + cuda静态编译
#下载最新的opencv git clone "https://github.com/opencv/opencv.git" git clone "https://gith ...
- 游戏mod启动器原理
基本原理 游戏程序会按一定顺序读取游戏文件夹根目录的文件. 所以我们制作mod和补丁的时候需要使得我们的文件先读取,从而使得后面读取到重复内容时候,游戏运行的内存中舍弃掉原本的文件. 游戏mod启动器 ...
- nginx的fastcgi配置
首先参考了一份配置注释(来自"小刚的博客"): #运行用户 user www-data; #启动进程,通常设置成和cpu的数量相等 worker_processes 1; #全局错 ...
- 记一次redis 基于spring实现类对同一个KEY序列化内容不同导致一次事故
我们的场景是这样的 我们对一个key:比如list.point.card:1 @Resourceprivate RedisTemplate<String, Long> redisTempl ...
- Allure测试报告完整学习笔记
目录 简介 安装Allure Allure测试报告的结构 Java TestNG集成Allure Report Python Pytest集成Allure Report 简介 假如你想让测试报告变得漂 ...