JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现

这两天在搞这个分布式事务,发现网上的seata资料大多数都是乐色,啥都不是,可能他都不知道register.conf和file.conf配置是干啥的,最后本人提供真实可用Demo,有问题可以私聊.
前一章节提过 2PC有两种实现一种是XA,XA是传统分布式事务解决方案,基于数据库必须要支持2PC协议,利用数据的2PC协议来实现分布式事务,而一种是seata,但seata同时支持AT和TCC,这里seata实现的AT,AT和XA执行流程一样,都是先准备提交,最后提交还是回滚都是有事务协调器来决定,XA和AT的不同之处在于而TCC模式是在预备阶段就以及提交本地事务,释放本地锁资源.

什么是2PC两阶段提交

2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase).
2是指两个阶段,P是指准备阶段,C是指提交阶段。

举例:张三和李四好久不见,老友约起聚餐,饭店老板要求先买单,才能出票。这时张三和李四分别抱怨近况不如
意,囊中羞涩,都不愿意请客,这时只能AA。只有张三和李四都付款,老板才能出票安排就餐。但由于张三和李四 都是铁公鸡,形成了尴尬的一幕:
准备阶段:老板要求张三付款,张三付款。老板要求李四付款,李四付款。 提交阶段:老板出票,两人拿票纷纷落座就餐。

例子中形成了一个事务,若张三或李四其中一人拒绝付款,或钱不够,店老板都不会给出票,并且会把已收款退 回。

整个事务过程由事务管理器和参与者组成,店老板就是事务管理器,张三、李四就是事务参与者,事务管理器负责
决策整个分布式事务的提交和回滚,事务参与者负责自己本地事务的提交和回滚。
在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议,如下图:

  1. 准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事 务,并写本地的Undo/Redo日志,此时事务没有提交。
    (Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数 据文件)
  2. 提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者 发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操
    作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。 下图展示了2PC的两个阶段,分成功和失败两个情况说明: 成功情况:

JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第1张图片

2PC主流的两种方案XA和Seata方案

  1. XA

这里就不讲解了,传统xa就是案例上将的
prepare阶段事务没提交,需要由事务管理器来确认事务是否提交,这样导致传统2PC会一直持有资源锁,性能差

  1. Seata

是由阿里中间件团队发起的开源项目 Fescar,后更名为Seata,它是一个是开源的分布式事务框架。

传统2PC的问题在Seata中得到了解决,它通过对本地关系数据库的分支事务的协调来驱动完成全局事务,是工作
在应用层的中间件。主要优点是性能较好,且不长时间占用连接资源,它以高效并且对业务0侵入的方式解决微服务场景下面临的分布式事务问题,它目前提供AT模式(即2PC)及TCC模式的分布式事务解决方案

Seata把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务
达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据库的本地事务,下 图是全局事务与分支事务的关系图:

JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第2张图片

Seata实现2PC与传统2PC的差别: 架构层次方面,传统2PC方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身,通过 XA协议实现,而 Seata的 RM 是以jar包的形式作为中间件层部署在应用程序这一侧的。
两阶段提交方面,传统2PC无论第二阶段的决议是commit还是rollback,事务性资源的锁都要保持到Phase2完成
才释放。而Seata的做法是在Phase1 就将本地事务提交,这样就可以省去Phase2持锁的时间,整体提高效率。

seata实现AT事务

本示例通过Seata中间件实现分布式事务,模拟三个账户的转账交易过程。
两个账户在三个不同的银行(张三在bank1、李四在bank2),bank1和bank2是两个个微服务。交易过程是,张三 给李四转账指定金额。

上述交易步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。
在这里插入图片描述

交互流程如下: 1、请求bank1进行转账,传入转账金额。 2、bank1减少转账金额,调用bank2,传入转账金额。

  1. 首先要下载事务协调器: https://github.com/seata/seata/releases/download/v0.7.1/seata-server-0.7.1.zip

  2. 解压启动: seata-server.bat -p 8888 -m file

    注:其中8888为服务端口号;file为启动模式,这里指seata服务将采用文件的方式存储信息。
    JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第3张图片
    如上图出现“Server started…”的字样则表示启动成功。

  3. 创建mysql

两个库 bank1和bank2 都创建account_info 和undo_log表

CREATE TABLE `account_info` (
  `id` bigint(20) DEFAULT NULL COMMENT 'id',
  `account_name` varchar(100) DEFAULT NULL COMMENT '户主姓名',
  `account_no` varchar(100) DEFAULT NULL COMMENT '户主id',
  `account_password` varchar(100) DEFAULT NULL COMMENT '帐户密码',
  `account_balance` double DEFAULT NULL COMMENT '帐户余额'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户表'
CREATE TABLE `undo_log` (
  `id` bigint(20) DEFAULT NULL COMMENT 'id',
  `branch_id` varchar(100) DEFAULT NULL COMMENT '分支id',
  `xid` varchar(100) DEFAULT NULL COMMENT '事务id',
  `context` varchar(100) DEFAULT NULL COMMENT '内容',
  `rollback_info` double DEFAULT NULL COMMENT '回滚信息',
  `log_status` double DEFAULT NULL COMMENT '日志状态',
  `log_created` double DEFAULT NULL COMMENT '日志创建',
  `log_modified` double DEFAULT NULL COMMENT '日志修改',
  `ext` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='undo log表'
注意 undo_log名称以及字段不可修改,这个是seata 的undo日志,在上面流程中提过,Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数 据文件
  1. 创建SpringCloud工程,我这里使用spring全家桶,你也可以用阿里cloud全家桶
    我这里使用SpringCloud最新版 Hoxton.SR5 SpringBoot使用2.3.1.RELEASE

这里就提示以下代码的作用以及关键处如何配置,源代码地址: https://github.com/cyf0477/seata-parent

说明一下注册中心还是Eureka的注册中心,没有任何改变

1.在bank1和bank2里分别加入seata依赖
JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第4张图片
2.在resource下加入registry.conf,这个conf需要在之前下载的seata服务器资源里的conf目录下copy

JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第5张图片
这里面需要关注两个文件,一个是register.conf,一个是file.conf ,这里就先讲讲这个register.conf和file.conf的作用,

  1. register.conf: 用于配置seata负载均衡方式,seata是分布式事务协调器,既然是分布式那么要实现高可用,必然需要有注册中心,下面就是默认register.conf内容,里面加有详细注释

		registry {
     
		  #注册中心选用的方式,默认是file,可以使用nacos ,eureka上面注释列举的所有方式都支持
		  #注意,如果这里注册中心原则使用file方式,那么就必须使用file.conf来配置file的一些配置,而且file模式不支持高可用
		  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
		  type = "file"  只能建立一个seata server
		
		  nacos {
     
		    serverAddr = "localhost"
		    namespace = "public"
		    cluster = "default"
		  }
		  eureka {
     
		    serviceUrl = "http://localhost:1001/eureka"    #这里我用的eureka,那么只需要修改eureka就可以
		    application = "default"   #seata注册到eureka的名称
		    weight = "1"    #权重
		  }
		  redis {
     
		    serverAddr = "localhost:6379"
		    db = "0"
		  }
		  zk {
     
		    cluster = "default"
		    serverAddr = "127.0.0.1:2181"
		    session.timeout = 6000
		    connect.timeout = 2000
		  }
		  consul {
     
		    cluster = "default"
		    serverAddr = "127.0.0.1:8500"
		  }
		  etcd3 {
     
		    cluster = "default"
		    serverAddr = "http://localhost:2379"
		  }
		  sofa {
     
		    serverAddr = "127.0.0.1:9603"
		    application = "default"
		    region = "DEFAULT_ZONE"
		    datacenter = "DefaultDataCenter"
		    cluster = "default"
		    group = "SEATA_GROUP"
		    addressWaitTime = "3000"
		  }
		  file {
     
		    name = "file.conf"
		  }
		}
		
		config {
          //conf只有下面列举的才需要修改   如果使用的eureka  下面配置无用
		  # file、nacos 、apollo、zk、consul、etcd3
		  type = "file"
		
		  nacos {
     
		    serverAddr = "localhost"
		    namespace = "public"
		    cluster = "default"
		  }
		  consul {
     
		    serverAddr = "127.0.0.1:8500"
		  }
		  apollo {
     
		    app.id = "seata-server"
		    apollo.meta = "http://192.168.1.204:8801"
		  }
		  zk {
     
		    serverAddr = "127.0.0.1:2181"
		    session.timeout = 6000
		    connect.timeout = 2000
		  }
		  etcd3 {
     
		    serverAddr = "http://localhost:2379"
		  }
		  file {
     
		    name = "file.conf"
		  }
		}
  1. file.conf: seata 注册中心指定为file file.conf才有意义,并且如果指定为file 类型,那么seata不支持高可用!,这里就不讲解file.conf里面的内容了,毕竟没一点意义
  2. 修改yml配置
spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group   #seata分组
seata:
  service:
    grouplist:
      default: ubuntu1804.wsl:8091  #seata服务地址  可以是多个  key的值我查看源码里没有要求指定什么名字,这里就随意了,如果seata集群,可以写多个服务地址

bank1 代表案例中的张三,先说一下数据源配置,让seata代理数据源,io.seata.rm.datasource.DataSourceProxy,这样seata才可能完全管理事务状态
JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第6张图片

需要在主事物分支开启@GlobalTransactional注解,在其他子事物分子是不需要这个注解

张三service
JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第7张图片
李四service

JAVA 强一致性 2PC两阶段提交介绍以及Seata AT模式实现_第8张图片

此时 seata at教程就完毕了 到这里有人可能有点混淆AT和XA AT是在seata里的概念等同于XA 都是两阶段提交解决方案 tcc解决方案使用hmily下章节讲解

附录

seata 官网 http://seata.io/zh-cn/docs/overview/what-is-seata.html

可以关注下博主的公众号,实时推送解决方案!
公众号

你可能感兴趣的:(分布式,事务,2PC,seata实现,2PC,2PC两阶段提交协议,两阶段提交,两阶段提交协议)