Apache ServiceComb Pack 微服务分布式数据最终一致性解决方案
https://github.com/OpenSagas-csharp/servicecomb-pack-csharp
Saga基本使用指南
使用前置条件说明
如果还有同学对Saga还不甚了解的同学,可以参考Saga官方中文地址地址,同时可以参考此项目贡献者之一的WithLin的一篇中文说明文章,该地址如下:地址,文章由浅入深的讲述了分布式事务在微服务场景下的重要性,以及Saga对分布式事务的大致实现方式和后续的思考
- 必须 你需要可用的一个本地或者远程的数据库(mysql或者postpresql)作为Saga持久化分布式事务事件的持久化存储,当然只要官方支持的Database Provider即可,具体idea数据库配置如下图,注意数据库的名字与您真实数据库名一致
- 必须 成功启动alpha-server,导致环境搭建以及部署颇为麻烦,后期官方将会提供image上传docker hub提供给大家使用,启动成功参考下图
可选 同时saga提供了UI可视化界面,直接idea中启动saga-web即可
支持docker-compose:根目录提供了一个dockercompose文件,只需要在工程的根目录下执行docker-compose up -d 即可,上面的操作可以给感兴趣的调试环境的搭建。
开始玩转分布式事务Saga
克隆当前项目,然后请使用VS2017打开解决方案定位到sample目录,你会看到如下所示的三个实例应用程序,这里app都是基于TargetFramework=netcoreapp2.0的,所以需要相应的启动环境
下面对上图做一个基本介绍,假定现在我们有三个微服务,分别是 Booking-预定服务,Car-订车服务,Hotel-酒店服务,相信大家一看便知,三者的从属关系以及在现实社会中的关联关系,下面我们的分布式事务一致性测试将在这几个app中完成,现在我们分别对项目做一定的初始化工作
services.AddOmegaCore(option =>
{
// your alpha-server address
option.GrpcServerAddress = "localhost:8080";
// your app identification
option.InstanceId = "Booking123";
// your app name
option.ServiceName = "Booking";
});
这里需要对三个项目都做如上所示的基本配置即可,现在一直都配置就绪了,下面开始我们的分布式事务的测试吧...
分布式事务场景测试
下面将会针对正常以及异常情况分别测试
正常情况测试
由 Booking 发起预定汽车和预定酒店的服务,且假设三个服务均可以正常访问的情况,正常启动如下图所示:
[HttpGet, SagaStart] // SagaStart 顾名思义标记为分布式事务开始的地方
[Route("book1")]
public ActionResult Book()
{
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car
httpClient.GetAsync("http://localhost:5002/api/values");
// book a hotel
httpClient.GetAsync("http://localhost:5003/api/values");
// your busniess code
// for example save the order to your database
return Ok("ok");
}
请求结果返回"ok",结果我们看看数据记录的是什么东西,相信也是大家比较关心的,看懂了数据库也就了解了分布式事务的阶段性事件存储,下面直接上图:
我们从上图很明显的就能看出来服务的先后关系以及服务之间的依赖关系,同时LocalTxId标记了每个一个过程的唯一ID,其中[type]需要注重说明一下,标记了每个动作了的状态同时也是判断每个微服务是否成功是否需要补偿的重要标准( 特别说明: sample项目中的预定车辆以及预定酒店是模拟操作,具体可以参见各自项目代码 )
节点服务异常情况
这里我为什么要说节点服务异常呢?相信经历过微服务的同学就知道,错综复杂的服务之间的调用,就会增加耦合以及某个节点服务出现异常导致整个调用连失败的情况,所以基于如此我们下面测试每个阶段所带来的情况分析
[HttpGet, SagaStart]
[Route("book1")]
public ActionResult Book1()
{
// throw new a exception for test
throw new DbUpdateException("I'm a dbUpdateException", new Exception());
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car
httpClient.GetAsync("http://localhost:5002/api/values").Wait();
// book a hotel
httpClient.GetAsync("http://localhost:5003/api/values").Wait();
return Ok("ok");
}
这里只是记录[Book1]本身服务的生命周期,因为还没有请求car和hotel,下面截图也验证我的预期结果:
测试Car-Service调用异常
这里需要特别说明的是,需要 httpClient 等待微服务调用结果,这样car-service出现调用异常,我们的框架才会感知到,才会上报通知事务管理,最后终止或者回滚事务链条
Omega.Sample.Booking -> ValuesController:
[HttpGet, SagaStart]
[Route("book2")]
public ActionResult Book2()
{
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car , this will be throw a exception from car-service
httpClient.GetAsync("http://localhost:5002/api/values").Wait();
// book a hotel
httpClient.GetAsync("http://localhost:5003/api/values").Wait();
return Ok("ok");
}
Omega.Sample.Car -> ValuesController :
[HttpGet]
public IEnumerable<string> Get()
{
CarBookingService carBookingService = new CarBookingService();
var carbook = new CarBooking()
{
Id = 1,
Amount = 1,
Name = "WithLin"
};
carBookingService.Order(carbook);
return new string[] { "value1", "value2" };
}
Omega.Sample.Car -> CarBookingService:
public class CarBookingService
{
private readonly ConcurrentDictionary<int, CarBooking> _bookings = new ConcurrentDictionary<int, CarBooking>(); [Compensable(nameof(CancelCar))]
public void Order(CarBooking carBooking)
{
carBooking.Confirm();
_bookings.TryAdd(carBooking.Id, carBooking);
// throw new Exception
throw new Exception("test car serivice error");
} void CancelCar(CarBooking booking)
{
_bookings.TryGetValue(booking.Id, out var carBooking);
carBooking?.Cancel();
}
}
果不其然我们的[Book2]方式直接向我们抛出了异常,上图说明:
那么再来看看数据是否和我们预期感觉一样讷,相信聪明的小伙伴应该知道套路是什么了:
关于 car-service 的红框状态描述相信大家就很清楚了,历经了开始->中止->结束,最后整个 Booking 方式完成
测试Hotel-Service调用异常
预期调用 Hotel-Service 异常,但是我们的 car-service 调用成功,这个时候我们需要 car-service 通过补偿的方式撤销调用 car-service 带来的数据或者状态的变化,达到要么全部成功,要么全部失败的结果,实现最终一致性
Omega.Sample.Booking -> ValuesController:
[HttpGet, SagaStart]
[Route("book")]
public async Task<ActionResult> Book()
{
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car
await httpClient.GetAsync("http://localhost:5002/api/values");
// book a hotel
await httpClient.GetAsync("http://localhost:5003/api/values");
return Ok("ok");
}
Omega.Sample.Car -> ValuesController :
[HttpGet]
public IEnumerable<string> Get()
{
CarBookingService carBookingService = new CarBookingService();
var carbook = new CarBooking()
{
Id = 1,
Amount = 1,
Name = "WithLin"
};
carBookingService.Order(carbook);
return new string[] { "value1", "value2" };
}
Omega.Sample.Car -> CarBookingService:
这里需要特别说明 [Compensable(nameof(CancelCar))] 此标记是指示补偿或者回滚时的方法,当然它的执行是在事务官发起通知的时候执行,具体参考下图断点命中
public class CarBookingService
{
private readonly ConcurrentDictionary<int, CarBooking> _bookings = new ConcurrentDictionary<int, CarBooking>(); [Compensable(nameof(CancelCar))]
public void Order(CarBooking carBooking)
{
carBooking.Confirm();
_bookings.TryAdd(carBooking.Id, carBooking);
//throw new Exception("test car serivice error");
} void CancelCar(CarBooking booking)
{
_bookings.TryGetValue(booking.Id, out var carBooking);
carBooking?.Cancel();
}
}
Omega.Sample.Hotel -> ValuesController :
[HttpGet]
public IEnumerable<string> Get()
{
HotelBookingService bookingService = new HotelBookingService();
HotelBooking hotelBooking = new HotelBooking()
{
Id = 1,
Amount = 10,
Name = "test"
};
bookingService.Order(hotelBooking);
return new string[] { "value1", "value2" };
}
Omega.Sample.Hotel -> HotelBookingService:
注意这里因为 booking.Amount > 2 将会触发异常导致服务调用错误
[Compensable(nameof(CancelHotel))]
public void Order(HotelBooking booking)
{
if (booking.Amount > 2)
{
throw new ArgumentException("can not order the rooms large than two");
}
booking.Confirm();
_bookings.TryAdd(booking.Id, booking);
}
Car-Service 补偿方法触发执行命中断点如下图:
数据库事务链条状态图(聪明的你观察到了那个补偿事件的记录了讷):
测试Booking-Service 最后调用异常
这里我们最后再来测试一下,如下情况,也是前面的 car-service hotel-service 调用都没有问题,但是最后 booking-service 提交的出现了未知异常(例如网络抖动.数据库闪断之类),代码我就不贴太多了,展示 booking-service 即可
[HttpGet, SagaStart]
[Route("book")]
public async Task<ActionResult> Book()
{
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car (no exception)
await httpClient.GetAsync("http://localhost:5002/api/values");
// book a hotel (no exception)
await httpClient.GetAsync("http://localhost:5003/api/values");
throw new Exception("just test unknown exception");
return Ok("ok");
}
理所当然的如下图所示:
关于 Command 表大致用来存储需要补偿的命令的,相应的alpha-server有定时服务在刷这个表,一直补偿成功为止,来保证最终的分布式事务的一致性
相信你已经注意到了我们的两个预定服务都触发了补偿逻辑(红框所示)
如何在服务超时情况维持分布式事务测试
[HttpGet, SagaStart(TimeOut = 3)]
[Route("book3")]
public ActionResult Book3()
{
// init basic httpclient
var httpClient = new HttpClient();
// mark a reservation of car , this will be throw a exception from car-service
httpClient.GetAsync("http://localhost:5002/api/values").Wait();
// book a hotel
httpClient.GetAsync("http://localhost:5003/api/values").Wait();
Thread.Sleep(5000);
return Ok("ok");
}
事件表详情 超时表详情 补偿表详情
Apache ServiceComb Pack 微服务分布式数据最终一致性解决方案的更多相关文章
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_4-2.微服务下登录检验解决方案 JWT讲解
笔记 2.微服务下登录检验解决方案 JWT讲解 简介:微服务下登录检验解决方案 JWT讲解 json wen token 1.JWT 是一个开放标准,它定义了一种用于简洁,自包含的用于通信双方 ...
- logback日志大量写磁盘导致微服务不能正常响应的解决方案
最近几天,遇到一个莫名其妙的问题,每天几乎同一时段微服务自己跑着跑着就假死了,过几个小时就又自动恢复了. 通过对定时任务.网卡.内存.磁盘.业务日志的排查分析,只有磁盘的IO在假死前一段时间偏高,经查 ...
- Apache ServiceComb 开源两周年,聊聊其与微服务的前世今生
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
- .Net Core with 微服务 - 分布式事务 - 可靠消息最终一致性
前面我们讲了分布式事务的2PC.3PC , TCC 的原理.这些事务其实都在尽力的模拟数据库的事务,我们可以简单的认为他们是一个同步行的事务.特别是 2PC,3PC 他们完全利用数据库的事务能力,在一 ...
- Java进阶专题(二十二) 从零开始搭建一个微服务架构系统 (上)
前言 "微服务"一词源于 Martin Fowler的名为 Microservices的,博文,可以在他的官方博客上找到http:/ /martinfowler . com/art ...
- SpringCloudAlibaba—微服务概念及SpringCloudAlibaba介绍
目录 1.1 系统架构演变 1.1.1 单体应用架构 1.1.2垂直应用架构 1.1.3 分布式架构 1.1.4 SOA架构 1.1.5 微服务架构 1.2 微服务架构介绍 1.2.1 微服务架构的常 ...
- SpringCloudAlibaba 微服务讲解(一)微服务介绍
微服务介绍 1.1 系统架构的演变 随若互联网的发展,网站应用的规模也在不断的扩大,逬而导致系统架构也在不断的进行变化.从互联 网早起到现在,系统架构大体经历了下面几个过程:单体应用架构一蟻直应用架构 ...
- 我眼中的ASP.NET Core之微服务 (二)
前言 接上一篇. 上一篇未完待续的原因是当时刚好是6-30号晚上马上12点了还没写完,然后我想赶在7月1号之前发出去,所以当时就发了.然后在发的时候出了一点问题,结果发出去的时候刚好是 7.1号 00 ...
- 微服务治理平台的RPC方案实现
导读:本文主要探讨了rpc框架在微服务化中所处的位置,需要解决的问题.同时介绍了用友云微服务治理平台的rpc解决方案,为什么选择该方案.该方案提供的好处是什么.同时也会介绍用友RPC框架的基本结构以及 ...
随机推荐
- Windows ----tasklist/taskkill
1) Tasklist命令详解 “Tasklist”命令是一个用来显示运行在本地或远程计算机上的所有进程的命令行工具,带有多个执行参数. 作用:结束一个或多个任务或进程.可以根据进程 ID 或图像名来 ...
- 可落地的DDD的(2)-为什么说MVC工程架构已经过时
摘要 mvc是一种软件设计模式,最早由Trygve Reenskaug在1978年提出,他有效的解决了表示层,控制器层,逻辑层的代码混合在一起的问题,很好的做到了职责分离.但是在实际的编码实践过程中, ...
- cygwin中修改path变量
1.在家目录建立 .bash_profile 文件. 2.在该文件添加: export PATH=/my/path/:$PATH 3.解释,/my/path/为你要添加的目录,为什么不在.bashrc ...
- 封装:Cmd命令调用和常用命令
原文:封装:Cmd命令调用和常用命令 一.Cmd命令调用方法 1.静态方法调用 class Program { static void Main(string[] args) { // Todo :打 ...
- NVIDIA-docker Cheatsheet
TensorFlow Docker requirements Install Docker on your local host machine. For GPU support on Linux, ...
- delphi安装控件
设置路径,要不就自己把文件copy到对应的文件夹里面再给你装一下装控件的 http://www.cppfans.com/vclcontrol.asp有关 VCL 控件的常见问题 (安装.卸载.升级BC ...
- 【转】Webpack 快速上手(中)
由于文章篇幅较长,为了更好的阅读体验,本文分为上.中.下三篇: 上篇介绍了什么是 webpack,为什么需要 webpack,webpack 的文件输入和输出 中篇介绍了 webpack 在输入和输出 ...
- LaTeX的安装并使其能够编译中文
首先,感谢博客园团队帮我找回这篇被我误删除的博客.找回方法:发送邮件至"contact@cnblogs.com",然后就可以在工作人员的帮助下找回了.下面介绍LaTeX的安装并使其 ...
- 第十周LINUX 学习笔记
LVS集群nat丶DR HA:高可用 平均无故障时间/(平均无故障时间+平均修复时间) 负载均衡 次序lb(负载)——>ha()LB tcp:lvs,haproxy 应用 ...
- LVS负载均衡部署
一.lvs-nat模式 1.1.环境介绍 本实验用三台虚拟机完成,一台虚拟机模拟lvs调度器,两块网卡,一块模拟公网一块模拟私网,公网地址192.168.0.201/24,私网地址192.168.4. ...