.Net Core with 微服务 - 使用 AgileDT 快速实现基于可靠消息的分布式事务

前面对于分布式事务也讲了好几篇了(可靠消息最终一致性分布式事务 - TCC分布式事务 - 2PC、3PC
https://github.com/kklldog/AgileDT 开源不易,大家多多 ✨✨✨

回顾

前面一篇文章(可靠消息最终一致性 )我们详细介绍了基于可靠消息的分布式事务。为了更好的理解 AgileDT 的代码,我们还是有必要简单的来回顾下。

c15b44ce77077789b922939bc135f846.png

该方案总体流程上可分为以下步骤:

  1. 主动方在真正的业务开始前先向可靠消息服务发送一个“待确认”的消息

  2. 可靠消息服务收到待确认消息后持久化消息到数据库

  3. 如果以上操作成功则主动方开始真正的业务,如果失败则直接放弃执行业务

  4. 如果业务执行成功则发送“确认”消息给可靠消息服务,如果执行失败则发送“取消”给可靠消息服务。

  5. 如果可靠消息服务收到“确认”消息则更新数据库里的消息记录的状态为“待发送”,如果收到的消息为“取消”则更新消息状态为“已取消”

  6. 如果上一步更新的数据库为“待发送”,那么会开始往MQ投递消息,并且更改数据库里的消息记录的状态为“已发送”

  7. 上一步往MQ投递消息成功后,MQ会给被动方推送消息。

  8. 被动方收到消息后开始处理业务

  9. 如果业务处理成功,则被动方对MQ进行ACK回复,则这条消息会从MQ内移除掉

  10. 如果业务处理成功,则发送“已完成”消息给可靠消息服务

  11. 可靠消息服务收到“已完成”消息后更新数据库消息记录未“已完成”

废话不多说了,下面让我们演示下如何使用 AgileDT 来快速实现一个基于可靠消息的分布式事务。
以下我们还是以经典的订单下单完成给会员赠送积分的场景来演示。

使用 AgileDT

依赖组件

  • mysql

  • rabbitmq

目前支持 mysql 数据库,但是数据访问组件使用的是 freesql 所以后续要实现支持别的数据库也很简单。目前框架使用的可靠消息服务为 rabbitmq 。

运行服务端

在服务新建一个数据库并且新建一张表

// crate event_message table on mysql
create table if not exists event_message
(event_id varchar(36) not nullprimary key,biz_msg varchar(4000) null,status enum('Prepare', 'Done', 'WaitSend', 'Sent', 'Finish', 'Cancel') not null,create_time datetime(3) null,event_name varchar(255) null
);

使用docker-compose运行服务端

version: "3"  # optional since v1.27.0
services:agile_dt:image: "kklldog/agile_dt"ports:- "5000:5000"environment:- db:provider=mysql- db:conn= Database=agile_dt;Data Source=192.168.0.115;User Id=root;Password=mdsd;port=3306- mq:userName=admin- mq:password=123456- mq:host=192.168.0.115- mq:port=5672

安装客户端

在主动方跟被动方都需要安装AgileDT的客户端库

Install-Package AgileDT.Client

主动方使用方法

  1. 在业务数据库添加事务消息表

// crate event_message table on mysql
create table if not exists event_message
(event_id varchar(36) not nullprimary key,biz_msg varchar(4000) null,status enum('Prepare', 'Done', 'WaitSend', 'Sent', 'Finish', 'Cancel') not null,create_time datetime(3) null,event_name varchar(255) null
);
  1. 修改配置文件

在appsettings.json文件添加以下节点:"agiledt": {"server": "http://localhost:5000","db": {"provider": "mysql","conn": "Database=agile_order;Data Source=192.168.0.125;User Id=dev;Password=dev@123f;port=13306"//"conn": "Database=agile_order;Data Source=192.168.0.115;User Id=root;Password=mdsd;port=3306"},"mq": {"host": "192.168.0.125",//"host": "192.168.0.115","userName": "admin","password": "123456","port": 5672}}
  1. 注入 AgileDT 客户端服务

public void ConfigureServices(IServiceCollection services){services.AddAgileDT();...}
  1. 实现IEventService方法
    处理主动方业务逻辑的类需要实现IEventService接口,并且标记那个方法是真正的业务方法。AgileDT在启动的时候会扫描这些类型,并且使用AOP技术生成代理类,在业务方法前后插入对应的逻辑来跟可靠消息服务通讯。这里要注意的几个地方:

  • 实现IEventService接口

  • 使用DtEventBizMethod注解标记业务入口方法

  • 使用DtEventName注解来标记事务的方法名称,如果不标记则使用类名

注意:业务方法最终一定要使用事务来同步修改消息表的status字段为done状态,这个操作框架没办法帮你实现
注意:业务方法如果失败请抛出Exception,如果不抛异常框架一律认为执行成功

public interface IAddOrderService:IEventService{bool AddOrder(Order order);}[DtEventName("orderservice.order_added")]public class AddOrderService : IAddOrderService{private readonly ILogger<AddOrderService> _logger;public AddOrderService(ILogger<AddOrderService> logger){_logger = logger;}public string EventId { get;set;}[DtEventBizMethod]public virtual bool AddOrder(Order order){var ret = false;//3. 写 Order 跟 修改 event 的状态必选写在同一个事务内FreeSQL.Instance.Ado.Transaction(() =>{order.EventId = EventId;//在订单表新增一个eventid字段,使order跟event_message表关联起来var ret0 = FreeSQL.Instance.Insert(order).ExecuteAffrows();var ret1 = FreeSQL.Instance.Update<OrderService.Data.entities.EventMessage>().Set(x => x.Status, MessageStatus.Done).Where(x => x.EventId == EventId).ExecuteAffrows();ret = ret0 > 0 && ret1 > 0;});return ret;}/// <summary>/// 构造后续业务处理需要的消息内容/// </summary>/// <returns></returns>public string GetBizMsg(){//这里可以构造传递到MQ的业务消息的内容,比如传递订单编号啊 ,以便后续的被动方处理业务时候使用var order = FreeSQL.Instance.Select<Order>().Where(x => x.EventId == EventId).First();return order?.Id;}}

在实现好 IAddOrderService 接口后,你可以像平常一样使用 IAddOrderService 来注入实现类。比如在 Controller 的构造函数注入进去。因为 AgileDT 在启动的时候会自动帮你注册。

注意:IAddOrderService 跟实现类的生命周期是 Scoped 。

被动方使用方法

  1. 在业务方数据库建表或者在业务表上加字段
    对于被动方来说这里不是必须要建一个表。但是至少要有个地方来存储event_id的信息,最简单的是直接在业务主表上加event_id字段。

  2. 修改配置文件

在appsettings.json文件添加以下节点:"agiledt": {"server": "http://localhost:5000","db": {"provider": "mysql","conn": "Database=agile_order;Data Source=192.168.0.125;User Id=dev;Password=dev@123f;port=13306"//"conn": "Database=agile_order;Data Source=192.168.0.115;User Id=root;Password=mdsd;port=3306"},"mq": {"host": "192.168.0.125",//"host": "192.168.0.115","userName": "admin","password": "123456","port": 5672}}
  1. 注入AgileDT服务

public void ConfigureServices(IServiceCollection services){services.AddAgileDT();...}
  1. 实现IEventMessageHandler接口
    被动方需要接收MQ投递过来的消息,这些处理类需要实现IEventMessageHandler接口。AgileDT启动的时候会去扫描这些类,然后跟MQ建立绑定关系。

  • 这里必须使用DtEventName注解标记需要处理的事件名称

  • Reveive 方法必须是幂等的

public interface IOrderAddedMessageHandler: IEventMessageHandler{}[DtEventName("orderservice.order_added")]public class OrderAddedMessageHandler: IOrderAddedMessageHandler{static object _lock = new object();public bool Receive(EventMessage message){var bizMsg = message.BizMsg;var eventId = message.EventId;string orderId = bizMsg;lock (_lock){var entity = FreeSQL.Instance.Select<PointHistory>().Where(x => x.EventId == eventId).First();if (entity == null){var ret = FreeSQL.Instance.Insert(new PointHistory{Id = Guid.NewGuid().ToString(),EventId = message.EventId,OrderId = orderId,Points = 20,CreateTime = DateTime.Now}).ExecuteAffrows();return ret > 0;}else{return true;}}}}

总结

通过以上演示,我们快速的实现了一个订单下单会员赠送积分的服务。可以看到使用 AgileDT 可以很快速的实现一个分布式事务。特别是在实现过一个分布式事务后,后面实现起来就特别简单,只要实现几个接口就可以了。AgileDT 才刚刚起步,希望大家多多支持,多多✨✨✨  ,多多 PR 。

https://github.com/kklldog/AgileDT

.Net Core with 微服务 - 可靠消息最终一致性分布式事务

.Net Core with 微服务 - 分布式事务 - TCC

.Net Core with 微服务 - 分布式事务 - 2PC、3PC

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一位汉子的恋爱心得

1 每天早上起床的你。。2 喂&#xff0c;你怎么不理我&#xff01;3 最新防撕家方法。。4 塑料姐妹花&#xff0c;离得远显脸小。。心疼左一&#xff01;5 国外一位汉子的恋爱心得……6 当有人跟你说“追星有什么用&#xff0c;那些人也不认识你”。。你点的每个赞&#xff0c;…

放大器非线性失真研究装置_高效布里渊光纤放大器

来源&#xff1a;PTB新闻 主要适用于以下领域&#xff1a;精确的光频传输&#xff0c;例如用于光学时钟的远距离比较 双向放大器对于在长距离光纤链路上传输超稳定的光频必不可少&#xff0c;它们可以补偿光损耗&#xff0c;大约每100千米20 dB。由德国联邦物理技术研究院(PTB)…

利用“多说”制作留言板、评论系统

留言板和评论系统在网站建设中会经常用到&#xff0c;今天为大家介绍如何利用多说来为自己的网站制作留言板、评论系统&#xff01;注意以下只是介绍一下简单的创建设置&#xff0c;更多功能大家可以自己去探索&#xff01; 1、进入多说网站 http://duoshuo.com/ 并且登录。 2、…

CSS边界属性的负值

在CSS中&#xff0c;如果边界属性取值为负值的时候&#xff0c;元素之间的关系就会因此而变得复杂很多。 在垂直方向上&#xff0c;两个元素的边界仍然会重叠&#xff0c;但是此时一个为正值&#xff0c;一个为负值&#xff0c;最后的取值并不是取其中较大的正值&#xff0c;而…

linux切换到docker目录,Linux更改Docker运行根目录的方法

许多Linux用户在安装系统的时候&#xff0c;并没有分配/var分区&#xff0c;而在安装Docker后才发现&#xff0c;它的默认存放位置是在/var/lib/docker。因此导致了Docker在运行的很慢&#xff0c;那么要怎么解决这个问题呢?下面一起来看看解决的方法吧。解决方法&#xff1a;…

10岁高分考上大学,16岁读博的95后神童张炘炀,活成了他想要的样子吗?

全世界只有3.14 % 的人关注了爆炸吧知识开启学神模式走上人生巅峰指日可待时间过得真快&#xff0c;今年的暑假没几天就要结束了&#xff0c;刚参加完高考的孩子也将步入大学&#xff0c;开启人生的新一段旅程。 说起上大学&#xff0c;这让超模君想起了少年天才、神童张炘炀。…

带有控制按钮的图片滚动

上一次写了一个图片自动滚动功能&#xff0c;没有左右按钮控制的功能。今天花了点时间&#xff0c;写了一个带有左右按钮控制的图片滚动效果。所谓自动滚动&#xff0c;原理就是周期性的执行一个效果。在js中&#xff0c;通常是用setInterval这个函数来执行的&#xff0c;setIn…

Win11代言人官宣

微软宣布李现为中国市场的“微软零售全品牌大使”&#xff0c;代言全新 Windows 11、Surface 和 Microsoft 365 等产品。微软全新发布的 Windows 11 操作系统&#xff0c;简洁高效&#xff0c;带来耳目一新的视觉和使用体验全新升级的新一代 Surface Pro 8 惊艳上市&#xff0…

求余运算符

笔记摘自《极客学院》 求余运算&#xff08;a % b&#xff09;是计算b的多少倍刚刚好可以容入a&#xff0c;返回多出来的那部分&#xff08;余数&#xff09;。 注意&#xff1a;求余运算&#xff08;%&#xff09;在其他语言也叫取模运算。然而严格说来&#xff0c;我们看该运…

万箭齐发!COSCon' 21深圳分会场闪亮登场!

“ 点击蓝字 / 关注我们 ”| 作者&#xff1a;COSCon21 组委会| 编辑&#xff1a;钱奕| 设计&#xff1a;朱亿钦| 责编&#xff1a;沈于蓝01序言世界上最遥远的距离&#xff0c;不是生与死的距离&#xff0c;而是你在我对面办公楼&#xff0c;却不能一起嗨皮。辣么多个辗转反侧…

6部BBC “教材级” 地理纪录片,有生之年必看系列!

全世界只有3.14 % 的人关注了爆炸吧知识看BBC的纪录片&#xff0c;既可以追溯上下数千年的历史文化&#xff0c;也可以欣赏从宇宙到地心深处的奇妙境界&#xff0c;而及其超级精彩的画面即使定格&#xff0c;也是一幅摄影佳作。BBC纪录片题材广泛、制作精良&#xff0c;观看起来…

黑马c++32期_【每日一考】第40期:计提折旧

每天都有很多小伙伴来做会计实操每日一考实操君看到非常的欣慰&#xff0c;希望大家持续打卡学习请看今天的题▼▼▼单选题某企业的一辆运货卡车&#xff0c;其原价为600 000元&#xff0c;预计总行驶里程为500 000千米&#xff0c;预计报废时的净残值率为5&#xff05;&#x…

C#提升性能的几点提示和技巧

C&#xff03;性能提示和技巧在Raygun[1]&#xff0c;我们是一群非常懂多种语言的开发人员。Raygun的各个部分使用不同的语言和框架编写-最好的工作方式。鉴于大量的C&#xff03;和我们正在处理的数据的爆炸性增长&#xff0c;在不同的时间需要进行一些优化工作。大部分重大的…

电脑睡眠快捷键_电脑快速进入睡眠的快捷键是什么?

电脑快速进入睡眠的快捷键是什么&#xff1f;正常情况下需要点击电源再按睡眠&#xff0c;小编觉得有点麻烦&#xff0c;如果你想要进入睡眠状态能够像锁屏快捷键那样就好了&#xff0c;那么今天就教大家如何设置睡眠快捷键&#xff0c;让你的电脑快速进入睡眠状态。我们知道电…

世界上最奇特的国界线,万万没想到...

全世界只有3.14 % 的人关注了爆炸吧知识你曾经可能多次在飞机上切换不同的国家&#xff0c;但未曾真的看见过这些国家之间的分界线。没见过之前&#xff0c;你脑海中的国界线是怎样的&#xff1f;一道高墙&#xff1f;还是一条无法逾越的鸿沟......看似正经而严谨&#xff0c;但…

Dell poweredge r210进BIOS修改磁盘控制器(SATA Controller)接口模式

Dell poweredge r210进BIOS修改磁盘控制器&#xff08;SATA Controller&#xff09;接口模式 开机后按F2键进入BIOS设置&#xff0c;如下图&#xff1a; BIOS设置主界面&#xff1a; 使用上下键移动光标到“SATA Controller”上&#xff0c;按回车键进入接口模式设置。 使用…

二叉排序树与文件操作的设计与实现_堆排序就这么简单

一、堆排序介绍来源百度百科&#xff1a;堆排序(Heapsort)是指利用堆积树&#xff08;堆&#xff09;这种数据结构所设计的一种排序算法&#xff0c;它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆&#xff0c;是完全二叉树。前面我已经…

Tp框架如何使用事务和锁,还有查询缓存

1.事务 在ThinkPHP框架中&#xff0c;可以使用think\db\Transaction类来实现事务。 use think\Db; use think\db\Transaction;// 开始事务 Db::startTrans();try {// 执行数据库操作Db::table(user)->where(id, 1)->update([name > John]);// 提交事务Db::commit(); }…

因为没钱买衣服,我女朋友不要我了......

1 我妈为了省钱啥事都做得出▼2 高端的黄牛总是用最朴素的方法▼3 不加点什么你们觉得我阴阳怪气祖安大师怎么办&#xff1f;▼4 一天共有86400秒一天写2000首诗&#xff0c;也就是平均43.2秒一首曹植七步成诗也不过如此了▼5 如果在猫顺毛时弄乱它的毛会怎么样&#xff1…

Tech UP——EGO北京分会成立啦

古人崇敬自然&#xff0c;认为天地生万物而四时有序&#xff0c;是以春耕、夏耘、秋收、冬藏。在过去的8年里&#xff0c;InfoQ中国秉持着“促进软件开发领域知识与创新的传播”的理念&#xff0c;始终与技术人和公司在一起。经过8年的耕耘和沉淀&#xff0c;正是给大家呈现一些…