微服务 -- 分布式事务

分布式事务

CAP定理

CAP定理是由加州大学伯克利分校Eric Brewer教授提出来的,他指出WEB服务无法同时满足一下3个属性:

一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效)
可用性(Availability) : 每个操作都必须以可预期的响应结束
分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成
具体地讲在分布式系统中,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性。

显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。

BASE理论

在分布式系统中,我们往往追求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢? 前人已经给我们提出来了另外一个理论,就是BASE理论,它是用来对CAP定理进行进一步扩充的。BASE理论指的是:

Basically Available(基本可用)
Soft state(软状态)
Eventually consistent(最终一致性)
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

什么是事务

事务是由一组操作组成的一个工作单元。怎么去理解这个问题呢?
我们从现实生活中去理解
那么事务有哪些特性呢?

事务特性
原子性:事务内部的一组操作要么同时成功,要么同时失败
隔离性:不同事务之间是互相不影响的
一致性:事务内部一组操作,各自操作产生的结果数据,要能够保证都是预期的状态
持久性:事务内部一组操作,各个操作产生的数据要能够持久的效应

什么是本地事务

本地事务就是由一组sql语句操作的集合,
本地事务主要就是指sql语句的操作

什么是分布式事务

分布式事务就是一组服务操作的集合
例如:在分布式系统或者微服务系统内,完成一个任何,需要涉及到多个服务来共同完成,这一组服务操作组成的集合,就是分布式事务

分布式事务类型

不同服务不同数据库

​ (微服务架构)微服务 -- 分布式事务_第1张图片

不同服务相同数据库

​ (微服务架构)
微服务 -- 分布式事务_第2张图片

相同服务不同数据库

(单体架构)​
微服务 -- 分布式事务_第3张图片

异步通信(消息队列)

为什么要使用分布式事务

我们从真实的项目场景中出发,例如我们现在学习的微服务架构,那么我们现在来研究一下为什么要使用分布式事务

目前我们的项目已经集成了4个微服务架构技术,那么我们目前来看一下,他们之间的调用逻辑
现在有这样一个需求,我想通过在团队聚合微服务里面添加团队信息和成员信息
如果在添加团队信息和成员信息,如果添加团队信息成功了,成员信息失败了,那么这个时候,就会出现团队添加成功,而成员信息就没有添加成功,是吧。那么,这个问题该如何解决呢?

方案,就是分布式事务。
那么分布式事务是如何解决的呢,

实现分布式事务几种方案

两阶段提交(2PC)

  • 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
  • 第二阶段:事务协调器要求每个数据库提交数据。

优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景

三阶段提交(3PC)

补偿事务(TCC)

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

  • Try 阶段主要是对业务系统做检测及资源预留

  • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。

  • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,耦合高

本地消息表(异步确保)

这种方案遵循BASE理论,采用的是最终一致性
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。

缺点: 消息表会耦合到业务系统中

Saga 事务模型

该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,
然后由 Saga 工作流引擎负责协调,
如果整个流程正常结束,那么就算是业务成功完成,
如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。

目前方案有很多,目前事务分为两类,一种是刚性事务,一种是柔性事务

刚性事务

就是完全遵守事务4大特性的分布式事务 ----- 主要体现在一致性(强一致性)
cap理论
2阶段提交
3阶段提交

柔性事务

就是不完全遵守事务4特性的分布式事务-----主要体现在一致性(不完全一直,最终一致性)
Base理论
特性
可查询操作:服务操作具有全局唯一标识,操作唯一确定的时间
幂等操作:重复调用多次产生的业务结果与调用一次产生的结果相同,一是通过业务操作实现幂等性,二是系统缓存所有请求与处理结果,最后是检测到重复请求之后,自动返回之前的处理结果。

同步事务(http,rpc)

Tcc分布式事务
Saga分布式事务

异步事务(消息队列MQ)

本地消息表

分布式事务方案演化

2阶段提交

1、准备阶段 prepare
2、提交阶段 commit
微服务 -- 分布式事务_第4张图片
优点
保证了数据的强一致性,适合对数据强一致性要求很高的关键领域
缺点
1、同步阻塞 性能问题
2、数据一致性问题
3、单点故障
4、实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景

场景
同服务不同数据库(单体架构)

3阶段提交

1、确认阶段 canCommit
2、准备阶段 preCommit
3、提交阶段 do commit
微服务 -- 分布式事务_第5张图片

优点
避免了单点问题,避免了阻塞问题
缺点
1、状态不一致
场景
同服务不同数据库(单体架构)

Tcc分布式事务
  1. Try 预操作阶段,没有真的执行操作,
  2. Confirm,真的执行操作
  3. Cancel,把预操作阶段的数据撤销

有3个状态, 未确认状态,确认状态,取消未确认状态
每个子事务都有各自的协调器,
最终一致性,有 人工处理和重试处理
TCC通信用rpc,
微服务 -- 分布式事务_第6张图片
优点
1.解决了跨服务的业务操作原子性问题,例如组合支付,订单减库存等场景非常实用
2.TCC的本质原理是把数据库的二阶段提交上升到微服务来实现,从而避免了数据库2阶段中锁冲突的长事务低性能风险。
3.TCC异步高性能,它采用了try先检查,然后异步实现confirm,真正提交的是在confirm方法中。
缺点
1.对微服务的侵入性强,微服务的每个事务都必须实现try,confirm,cancel等3个方法,开发成本高,今后维护改造的成本也高。
2.为了达到事务的一致性要求,try,confirm、cancel接口必须实现等幂性操作。
(定时器+重试)
3.由于事务管理器要记录事务日志,必定会损耗一定的性能,并使得整个TCC事务时间拉长,建议采用redis的方式来记录事务日志。

场景:微服务

Saga分布式事务
  1. 直接执行业务阶段
  2. 直接取消阶段
    优化了TCC方案 微服务 -- 分布式事务_第7张图片
    优点
    1、避免服务之间的循环依赖,因为saga协调器会调用saga参与者,但参与者不会调用协调器
    2、集中分布式事务编排
    3、降低参与者的复杂性
    4、回滚更容易管理
    Saga模式的一大优势是它支持长事务。因为每个微服务仅关注其自己的本地原子事务,所以如果微服务运行很长时间,则不会阻止其他微服务。这也允许事务继续等待用户输入。此外,由于所有本地事务都是并行发生的,因此任何对象都没有锁定。

缺点
协调器集中太多逻辑的风险
Saga模式很难调试,特别是涉及许多微服务时。此外,如果系统变得复杂,事件消息可能变得难以维护。
Saga模式的另一个缺点是它没有读取隔离。例如,客户可以看到正在创建的订单,但在下一秒,订单将因补偿交易而被删除

场景:微服务

Saga分布式事务框架

主要用在微服务
1、Eventuate-Tram-Saga JDBC/JPA的java微服务的Saga框架
​ 目前c# net没有支持的客户端,不好使用
2、ServiceComb Pack
​ 是华为开源的一个微服务框架 后进入Apache软件基金会孵化,
servicecomb是华为开源的一个微服务框架,后进入Apache软件基金会孵化,现已毕业,是apache顶级开源项目,而servicecomb-pack是servicecomb孵化的三个子项目之一,是分布式事务最终一致性解决方案,0.3.0版本之前叫saga,现改名为servicecomb-pack,支持saga和tcc两种分布式事务协议,相关理论知识请移步(传送门),本文主要介绍saga模式

微服务项目中如何使用ServiceComb Pack

特性
  • 高可用:支持高可用的集群模式部署。
  • 高可靠:所有的关键事务事件都持久化存储在数据库中。
  • 高性能:事务事件是通过高性能gRPC来上报的,且事务的请求和响应消息都是通过Kyro进行序列化和反序列化。
  • 低侵入:仅需2-3个注解和编写对应的补偿方法即可引入分布式事务。
  • 部署简单:支持通过容器(Docker)进行快速部署和交付。
  • 补偿机制灵活:支持前向恢复(重试)及后向恢复(补偿)功能。
  • 扩展简单:基于Pack架构很容实现多种协调协议,目前支持TCC、Saga协议,未来还可以添加其他协议支持。
内部概念

ServiceComb Pack 架构是由 alphaomega组成,其中:

  • alpha充当协调者的角色,主要负责对事务进行管理和协调。
  • omega是微服务中内嵌的一个agent,负责对调用请求进行拦截并向alpha上报事务事件。
微服务项目和ServiceComb Pack关系图

ServiceComb Pack微服中如何解决事务问题
微服务 -- 分布式事务_第8张图片
微服务中servicecomb-pack-csharp-omega 运行原理
微服务 -- 分布式事务_第9张图片

微服务项目中如何使用ServiceComb Pack

微服务 -- 分布式事务_第10张图片

使用条件

1、微服务项目
2、ServiceComb Pack saga事务框架alpha,协调器
3、servicecomb-pack-csharp-omega_v0.1,saga事务框架客户端omega,客户端
4、mysql或者PostgreSQL

使用步骤

1、微服务项目操作
​ 1.1 启动微服务项目,正确响应出结果
2、alpha操作
​ 2.1 环境准备​

  • jdk1.8
    下载地址http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ​ PostgreSQL
    下载地址:https://www.enterprisedb.com/downloads/postgres-postgresql-downloads。
    Mysql
    下载地址:https://dev.mysql.com/downloads/mysql/ ​
    ServiceComb Pack
    下载地址:http://servicecomb.apache.org/release/pack-downloads/ ​
    Mysql和PostgreSQL二者选一,默认是PostgreSQL

​2.2 ServiceComb Pack 启动 (连接mysql为例)
​ 2.2.1 PostgreSQL下启动ServiceComb Pack
​ \alpha\apache-servicecomb-pack-distribution-0.5.0-bin 使用cmd

java -D"spring.profiles.active=prd" -D"spring.datasource.url=jdbc:postgresql://localhost:5432/saga?useSSL=false" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -jar alpha-server-0.5.0-exec.jar

​ 2.2.2 mysql配置
​ 1、在apache-servicecomb-pack-distribution-0.5.0-bin目录下创建插件目录
​ plugins文件夹
​ 2、 在plugins文件夹内部添加mysql驱动
​ mysql-connector-java-8.0.15.jar
​ 3、mysql下启动ServiceComb Pack
​ 切换到什么目录 \alpha\apache-servicecomb-pack-distribution-0.5.0-bin 使用cmd

java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -jar alpha-server-0.5.0-exec.jar

​ 4、查询mysql saga数据库
​ 事务相关表信息
​ txexvent- 事件表详情
​ txtimeout-超时表详情
​ compenste-补偿表详情
3、omega操作

​ 3.1 环境准备

​ servicecomb-pack-csharp-omega_v0.1
下载地址:https://github.com/OpenSagas-csharp/servicecomb-pack-csharp#servicecomb-pack-csharp
https://github.com/OpenSagas-csharp/servicecomb-pack-csharp
​ 3.2 omega配置

​ 1、在RuanMou.MicroService下面创建解决方案文件夹
​ omega文件夹
​ 2、在omega文件下面从导入项目
​ 3、在AggregateService,MemberService,TeamService下分别引入

​ 4、在AggregateService,MemberService,TeamService中从Servicecomb.Saga.Omega.Core中复制AssemblyInfo.cs 到根目录下
​ AssemblyInfo.cs
​ 5、在AggregateService,MemberService,TeamService项目startup.cs引入

      public void ConfigureServices(IServiceCollection services)
        {
            // 7、注册saga分布式事务
            services.AddOmegaCore(option =>
            {
                option.GrpcServerAddress = "localhost:8080"; // 1、协调中心地址
                option.InstanceId = "AggregateService-1";// 2、服务实例Id
                option.ServiceName = "AggregateService";// 3、服务名称
            });
            services.AddControllers();
        }

​ 6、在AggregateService项目AggregateController.cs文件中添加SagaStart

        /// 
        /// 添加团队和成员信息
        /// 
        /// 
        [HttpPost, SagaStart]
        public ActionResult Post(string value)
        {
            Console.WriteLine($"添加团队信息和成员信息");
            // 1、添加团队
            var team = new Team() { Name = "研发"};
            teamServiceClient.InsertTeams(team);
        // 2、添加团队成员
        var member = new Member() { FirstName ="tony",NickName="tony-1", TeamId = team.Id};
        memberServiceClient.InsertMembers(member);

        return Ok("添加成功");
    }	

​ 7、在MemberService项目中MemberServiceImpl.cs文件下添加

  /// 
        /// saga事务参与者 Compensable撤销业务 逻辑
        /// 
        /// 
        [Compensable(nameof(DeleteMember))]
        public void Create(Member member)
        {
            memberRepository.Create(member);
        }

        void DeleteMember(Member member)
        {
            memberRepository.Delete(member);
        }

​ 8、在TeamService项目中TeamServiceImpl.cs文件下添加

 /// 
 /// saga事务参与者 Compensable撤销业务 逻辑
 /// 
 /// 
 [Compensable(nameof(DeleteTeam))]
 public void Create(Team team)
 {
 	teamRepository.Create(team);
 }
   /// 
    /// 撤销方法
    /// 
    /// 
    void DeleteTeam(Team team)
    {
        teamRepository.Delete(team);
    }	
正常情况

全部执行成功的情况下面

节点服务异常情况

1、TeamService服务异常

​ 1.1 代码配置

 /// 
        /// saga团队提交和撤销
        /// 
        /// 
        [Compensable(nameof(DeleteCompensable))]
        public void Create(Team team)
        {
            teamRepository.Create(team);
// 1、异常情况测试
        throw new Exception("TeamService出现异常");
    }

    /// 
    /// 撤销方法
    /// 
    /// 
    void DeleteCompensable(Team team)
    {
        teamRepository.Delete(team);
    }	

·1.2 效果展示

2、MemberServce服务异常

​ 2.1 在 MemberServce服务MemberServceImpl.cs中添加

       /// 
        /// saga事务参与者 Compensable撤销业务逻辑
        /// 
        /// 
        [Compensable(nameof(DeleteMember))]
        public void Create(Member member)
        {
            memberRepository.Create(member);
       // 1、异常情况测试
        throw new Exception("MemberService出现异常");
    }

    void DeleteMember(Member member)
    {
        memberRepository.Delete(member);
    }	

​ 2.2 效果展示

3、AggregateService服务异常

​ 3.1 代码配置

        /// 
        /// 添加团队和成员信息
        /// 
        /// 
        [HttpPost, SagaStart]
        public ActionResult Post(string value)
        {
            Console.WriteLine($"添加团队信息和成员信息");
            // 1、添加团队
            var team = new Team() { Name = "研发"};
            teamServiceClient.InsertTeams(team);
        // 2、添加团队成员
        var member = new Member() { FirstName ="tony",NickName="tony-1", TeamId = team.Id};
        memberServiceClient.InsertMembers(member);

        // 3、添加异常
        throw new Exception("AggregateService服务异常");
        return Ok("添加成功");
    }			

​ 3.2 效果展示

节点超时情况

1、AggregateService超时

​ 1.1 代码配置

        /// 
        /// 添加团队和成员信息
        /// 
        /// 
        [HttpPost, SagaStart(TimeOut =5)]
        public ActionResult Post(string value)
        {
            Console.WriteLine($"添加团队信息和成员信息");
            // 1、添加团队
            var team = new Team() { Name = "研发"};
            teamServiceClient.InsertTeams(team);
        // 2、添加团队成员
        var member = new Member() { FirstName ="tony",NickName="tony-1", TeamId = team.Id};
        memberServiceClient.InsertMembers(member);

        // 3、节点超时
        Thread.Sleep(10);
        return Ok("添加成功");
    }

​ 1.2 结果展示​

原理展示

微服务 -- 分布式事务_第11张图片

微服务 -- 分布式事务_第12张图片

微服务 -- 分布式事务_第13张图片

ServiceComb Pack集群

步骤

1、ServiceComb Pack 实例1

切换到什么目录 \apache-servicecomb-pack-distribution-0.5.0-bin 使用cmd

java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"alpha.server.port=8080" -D"server.port=8090"  -jar alpha-server-0.5.0-exec.jar

2、ServiceComb Pack 实例2

切换到什么目录 \apache-servicecomb-pack-distribution-0.5.0-bin 使用cmd

java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"alpha.server.port=8080" -D"server.port=8090"  -jar alpha-server-0.5.0-exec.jar

3、ServiceComb Pack 实例2

切换到什么目录 \apache-servicecomb-pack-distribution-0.5.0-bin 使用cmd

java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"alpha.server.port=8080" -D"server.port=8090"  -jar alpha-server-0.5.0-exec.jar

4、分别连接到不同的集群进行操作数据库

ServiceComb Pack 注册到consul

条件

1、consul

步骤

1、配置consul

java -D"spring.profiles.active=mysql" -D"loader.path=./plugins" -D"spring.datasource.url=jdbc:mysql://localhost:3306/saga?useSSL=false&serverTimezone=Asia/Shanghai" -D"spring.datasource.username=root" -D"spring.datasource.password=root" -D"spring.cloud.consul.host=http://127.0.0.1" -D"spring.cloud.consul.port=8500" -D"spring.cloud.consul.enabled=true" -jar alpha-server-0.5.0-exec.jar

2、consul客户端查看

你可能感兴趣的:(microservice)