当前位置:首页 > 科技  > 软件

Seata如何实现两阶段提交(2PC)分布式事务

来源: 责编: 时间:2024-01-26 17:08:09 349观看
导读介绍2PC,全称为两阶段提交(Two-Phase Commit),是一种在分布式系统中用来保证事务原子性和一致性的协议。它主要用于协调分布式数据库或分布式事务环境中的多个参与者,确保所有参与者要么一起成功提交事务,要么一起回滚事务,

介绍

2PC,全称为两阶段提交(Two-Phase Commit),是一种在分布式系统中用来保证事务原子性和一致性的协议。它主要用于协调分布式数据库或分布式事务环境中的多个参与者,确保所有参与者要么一起成功提交事务,要么一起回滚事务,以保持数据的一致性。qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

在2PC协议中有两个主要阶段:qsZ28资讯网——每日最新资讯28at.com

  1. 准备阶段(Prepare Phase):

事务协调器接收到发起事务的客户端请求后,向所有参与该事务的资源管理器(例如数据库、服务节点等)发送“准备提交”请求。qsZ28资讯网——每日最新资讯28at.com

每个资源管理器执行事务操作,并将事务相关的更改锁定但不提交,然后回复事务协调器它们是否准备好提交事务(根据各自是否能够成功完成事务而定)。qsZ28资讯网——每日最新资讯28at.com

  1. 提交阶段(Commit Phase):
  • 如果事务协调器收到了所有资源管理器的肯定答复,即所有参与者都准备好提交事务,则向所有参与者发出“正式提交”指令。qsZ28资讯网——每日最新资讯28at.com

  • 若协调器收到任何一个参与者的否定响应,或者在等待超时后仍有参与者未响应,则向所有参与者发出“回滚事务”的指令。qsZ28资讯网——每日最新资讯28at.com

通过这种方式,2PC确保了所有节点要么全部完成事务,要么全部撤销事务,从而维护了分布式环境下的事务原子性。然而,2PC也存在一些缺点,比如单点故障问题(即事务协调器宕机可能导致事务长期阻塞)、网络分区情况下的不确定性以及性能上的潜在瓶颈。qsZ28资讯网——每日最新资讯28at.com

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

图片图片qsZ28资讯网——每日最新资讯28at.com

与 传统2PC 的模型类似,Seata定义了3个组件来协议分布式事务的处理过程qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

  • Transaction Coordinator (TC):事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各各分支事务的提交或回滚。
  • Transaction Manager (TM):事务管理器,TM需要嵌入应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。
  • Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分支(本地)事务的提交和回滚。

具体实现

案例分析:两个账户在不同的银行(张三在bank1、李四在bank2),bank1和bank2是两个微服务。交易过程是,张三给李四转账指定金额。qsZ28资讯网——每日最新资讯28at.com

上述交易步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

为了简化环境搭建,小编这里采用file启动seata,项目搭建也只是两个普通的SpringBoot项目,未使用微服务。qsZ28资讯网——每日最新资讯28at.com

下载seata服务器

官方下载地址:https://github.com/seata/seata/releasesqsZ28资讯网——每日最新资讯28at.com

  1. registry.type=file:

registry.type=file 其类型设置为 file 时,意味着 Seata 的服务注册中心不依赖于外部的如 Nacos、Eureka、Zookeeper 等第三方注册中心,而是使用本地文件的方式来存储和管理服务节点信息。这种模式主要用于快速测试或简单的单机部署场景,因为在这种模式下无法自动发现和管理集群环境中的其他 Seata Server 节点,不具备高可用性。qsZ28资讯网——每日最新资讯28at.com

  1. config.type=file:
  • config.type=file 表示 Seata 使用本地文件作为配置源。这意味着 Seata 会从指定的本地文件中读取全局事务协调器(TC)、事务管理器(TM)和资源管理器(RM)等组件所需的配置信息,而不是通过Nacos、Apollo或其他远程配置中心获取配置。这种方式同样适用于快速验证和简单部署情况,实际生产环境中可能需要结合分布式配置中心来动态更新和管理配置。qsZ28资讯网——每日最新资讯28at.com

  • seata安装初始化参考《SpringCloud Alibaba微服务实战之环境准备》,注意本次启动是采用file方式启动
  • seata启动:/bin/seata-server.bat -m file

图片图片qsZ28资讯网——每日最新资讯28at.com

  • bank-1 和 bank-2启动:

图片图片qsZ28资讯网——每日最新资讯28at.com

bank-1 和 bank-2服务搭建

库表建立

CREATE DATABASE `bank1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';CREATE TABLE `account_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名',`account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号',`account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码',`account_balance` double NULL DEFAULT NULL COMMENT '帐户余额',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;INSERT INTO `account_info` VALUES (2, '张三的账户', '1', '', 10000);
CREATE DATABASE `bank2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';CREATE TABLE `account_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名',`account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号',`account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码',`account_balance` double NULL DEFAULT NULL COMMENT '帐户余额',PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;INSERT INTO `account_info` VALUES (3, '李四的账户', '2', NULL, 0);

备注:分别在bank1、bank2库中创建undo_log表,此表为seata框架使用qsZ28资讯网——每日最新资讯28at.com

依赖引入

<dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>io.seata</groupId>            <artifactId>seata-spring-boot-starter</artifactId>            <version>1.4.2</version>        </dependency>        <dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus-boot-starter</artifactId>            <version>3.4.1</version>        </dependency>        <!--mysql-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.47</version>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>        <!--bank-2 不需要-->        <dependency>            <groupId>org.apache.httpcomponents</groupId>            <artifactId>httpclient</artifactId>        </dependency>    </dependencies>

定义配置

server:  port: 8081  #port: 8082spring:  application:    name: bank-1    #name: bank-2  datasource:    url: jdbc:mysql://localhost:3306/bank1?characterEncoding=utf8&useSSL=false    #url: jdbc:mysql://localhost:3306/bank2?characterEncoding=utf8&useSSL=false    driver-class-name: com.mysql.jdbc.Driver    username: root    password: rootseata:  tx-service-group: order_tx_group #自定义事务组名称需要与seata-server中的对应  service:    vgroup-mapping:      order_tx_group: default # TC 集群(必须与seata-server保持一致)

定义mapper

# bank-1@Update("update account_info set account_balance = account_balance + #{amount} where account_no = #{accountNo}")int updateAccountBalance(@Param("accountNo") String accountNo, @Param("amount") Double amount);# bank-2@Update("UPDATE account_info SET account_balance = account_balance + #{amount} WHERE account_no = #{accountNo}")int updateAccountBalance(@Param("accountNo") String accountNo, @Param("amount") Double amount);

服务调用

bank-1:qsZ28资讯网——每日最新资讯28at.com

@GlobalTransactional    @Override    public void updateAccountBalance(String accountNo, Double amount) {        log.info("******** Bank1 Service Begin ... xid: {}" , RootContext.getXID());        //张三扣减金额        baseMapper.updateAccountBalance(accountNo,amount * -1);        //向李四转账        CloseableHttpClient httpclient = HttpClients.createDefault();        HttpGet httpget = new HttpGet("http://localhost:8082/bank2/transfer?amount="+amount);        httpget.addHeader(RootContext.KEY_XID,RootContext.getXID());        try{            CloseableHttpResponse response = httpclient.execute(httpget);            HttpEntity entity = response.getEntity();            String result = EntityUtils.toString(entity);            log.info("bank2 服务返回结果:"+result);        }catch (Exception e){            throw new RuntimeException("bank2 服务异常");        }        //人为制造错误        if(amount > 100){            throw new RuntimeException("bank1 make exception amount > 100");        }    }

当业务方法开启全局异常处理器后,TM注册到TC获取到一个XID,此时在业务中,服务远程访问时,此XID会被下面分支业务方法RM接收到,当各个方法处理完成后RM会向TC直接交互把结果通过XID通知给TC,最后业务方法结束后,TM会通知TC业务已经完成,TC会根据RM通知的结果来通知各个RM提交或者回滚。但是在分布式事务中,入口TM传出时不会将XID放入请求头中向其他服务传递,这样就导致全局异常捕获失效,因此需要手动将XID设置到请求头中,携带给各分支业务来避免事务失效问题。qsZ28资讯网——每日最新资讯28at.com

bank-2:qsZ28资讯网——每日最新资讯28at.com

@Transactional    @Override    public void updateAccountBalance(String accountNo, Double amount) {        log.info("******** Bank2 Service Begin ... xid: {}" , RootContext.getXID());        //李四增加金额        baseMapper.updateAccountBalance(accountNo,amount);        //制造异常        if(amount < 100){            throw new RuntimeException("bank1 make exception amount < 100");        }    }

服务配置seata

file.conf:qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

registry.conf:qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

执行流程

正常流程:qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

回滚流程:qsZ28资讯网——每日最新资讯28at.com

图片图片qsZ28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-69008-0.htmlSeata如何实现两阶段提交(2PC)分布式事务

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: React 19 即将推出的四个全新 Hooks,很实用!

下一篇: 聊聊什么是JSX以及在React中的使用

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • 直屏旗舰来了 iQOO 12和K70 Pro同台竞技

    旗舰机基本上使用的都是双曲面屏幕,这就让很多喜欢直屏的爱好者在苦等一款直屏旗舰,这次,你们等到了。据博主数码闲聊站带来的最新爆料称,Redmi下代旗舰K70 Pro和iQOO 12两款手
  • 小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    疫情带来了网课,网课盘活了安卓平板,安卓平板市场虽然中途停滞了几年,但好的一点就是停滞的这几年行业又有了新的发展方向,例如超窄边框、高刷新率、多摄镜头组合等,这就让安卓
  • 三言两语说透柯里化和反柯里化

    JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是两种很有用的技术,可以帮助我们写出更加优雅、泛用的函数。本文将首先介绍柯里化和反柯里化的概念、实现原理和应用
  • 在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 腾讯VS网易,最卷游戏暑期档,谁能笑到最后?

    作者:无锈钵来源:财经无忌7月16日晚,上海1862时尚艺术中心。伴随着幻象的精准命中,硕大的荧幕之上,比分被定格在了14:12,被寄予厚望的EDG战队以绝对的优势战胜了BLG战队,拿下了总决
  • 签约井川里予、何丹彤,单视频点赞近千万,MCN黑马永恒文希快速崛起!

    来源:视听观察永恒文希传媒作为一家MCN公司,说起它的名字来,可能大家会觉得有点儿陌生,但是说出来下面一串的名字之后,或许大家就会感到震惊,原来这么多网红,都签约这家公司了。根
  • 华为Mate 60系列用上可变灵动岛:正式版体验将会更出色

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
Top