spring cloud之分布式事务

news2024/11/17 13:55:06

写在前面

1:分布式事务介绍

参考MySQL之分布式事务 。

2:seata实战

架构图:
在这里插入图片描述
可以看到seata在这里作为协调者的角色,协调所有事务的提交以及回滚,其中seata使用MySQL存储每个分支事务的执行状态信息,以便在需要回滚等操作时可以获取到相应的信息进行回滚操作。这里seata服务也是作为一个微服务节点来运行,因此也需要将信息注册到nacos中,这样可以方便的来做服务发现。

2.1:搭建seata服务

首先在这里 现在seata的运行jar包,在运行jar包之前还需要做一些配置操作。

  • 首先持久化模式
    这里我们使用的持久化模式是MySQL,修改如下:
## transaction log store, only used in server side
store {
  ## store mode: file、db
  ## 【改动点01】 - 替换成db类型
  mode = "db"
  • 接着对应的修改数据库连接信息
store {
  mode = "db"

  ## 【改动点02】 - 更改参数
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
    user = "root"
    password = ""
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  }
}
  • 创建seata数据库和表
    注意创建在seata数据库中。
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME,
    `gmt_modified`      DATETIME,
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(96),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;
  • 创建undo_log
    注意该表创建在应用对应的数据库中,咱们这里就是geekbang_coupon_db,用来存储分支事务已经提交的事务的回滚操作信息:
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT COMMENT 'increment id',
    `branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(100) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME     NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME     NOT NULL COMMENT 'modify datetime',
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
  • 开启服务发现
    配置注册自身信息到nacos中:
registry {
  # 【改动点01】 - type变成nacos
  type = "nacos"

  # 【改动点02】 - 更换
  nacos {
    application = "seata-server-dyq"
    serverAddr = "192.168.10.62:8858"
    group = "myGroup"
    namespace = "dev"
    cluster = "default"
    username = ""
    password = ""
  }

}

注意这里的group和namespace和咱们的应用保持一致,最后我们就可以通过bin目录下的启动脚本来启动应用了:

D:\programs\seata\seata\seata-server-1.4.2\bin>seata-server.bat
[0.003s][warning][gc] -Xloggc is deprecated. Will use -Xlog:gc:D:\programs\seata\seata\seata-server-1.4.2\bin\\../logs/seata_gc.log instead.
[0.069s][info   ][gc] Using G1

如果一切正常的话就可以在nacos中看到服务信息了:
在这里插入图片描述

2.2:seata AT模式

AT模式的角色如下:

TC:transaction coordinator,即seata server,扮演协调者的角色,负责协调全局事务的提交和回滚,维护全局和分支事务的状态。
TM:transaction manager,发一起一个全部事务,并对全局事务的提交和回滚进行决议,在AT方案中,TM由发起事务的微服务扮演。
RM:resource manager,资源管理器,向TC上报分支事务的状态,负责分支事务的提交和回滚,由参与的微服务扮演。

以custom模块调用template模块为例看下具体处理过程,可参考下图:
在这里插入图片描述
主要分为两个阶段:

1:各个微服务模块注册分支事务信息到seata,执行CRUD操作,seata会根据具体的操作生成对应的分支事务的回滚操作,并将信息存储到表undo_log中
2:如果是TM最终决议commit,则seata通知所有的rm提交事务,并删除undo_log表中的回滚数据,如果是TM最终决议rollabck,则所有的RM执行undo_log的回滚操作,回滚修改,最终也会删除undo_log的回滚数据。

接着我们来改造微服务,首先在custom和template模块中引入依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

和添加配置:

spring:
  ...
  cloud:
    alibaba:
      seata:
        tx-service-group: seata-server-group
seata:
  application-id: coupon-template-serv-stream
  registry:
    type: nacos
    nacos:
      application: seata-server-dyq
      server-addr: 192.168.10.62:8868
      namespace: dev
      group: myGroup
      cluster: default
  service:
    vgroup-mapping:
      seata-server-group: default

接着在custom添加测试的接口,具体代码参看源码,用来调用template服务删除服务,做如下的事情:

1:custom调用template将coupon_template表对应templateId的信息标记为删除
2:cusotm服务本身标记coupon表tempalteId对应的信息标记为无效

首先在接口添加注解@GlobalTransactional(name = "coupon-customer-serv", rollbackFor = Exception.class),如下:

@DeleteMapping("templateCouponTemplate")
@GlobalTransactional(name = "coupon-customer-serv", rollbackFor = Exception.class)
public void templateCouponTemplate(@RequestParam("templateId") Long templateId) {
    customerService.deleteCouponTemplate(templateId);
}

来手动抛个异常:

@Override
@Transactional
public void deleteCouponTemplate(Long templateId) {
    templateService.deleteTemplate(templateId);
    couponDao.deleteCouponInBatch(templateId, CouponStatus.INACTIVE);
    // 模拟分布式异常
    throw new RuntimeException("AT分布式事务挂球了");
}

为了查看相关表生成的记录,我们可以在异常这行打个debug,然后看下数据,此时相当于custom调用template返回,custom的本地事务还没有提交时:
在这里插入图片描述
在这里插入图片描述
其中undo_log存储了回滚需要的信息,以json格式存储:

{
    "@class": "io.seata.rm.datasource.undo.BranchUndoLog",
    "xid": "10.77.0.33:8091:7638587481204744198",
    "branchId": 7638587481204744199,
    "sqlUndoLogs": [
        "java.util.ArrayList",
        [
            {
                "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
                "sqlType": "UPDATE",
                "tableName": "coupon_template",
                "beforeImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "coupon_template",
                    "rows": [
                        "java.util.ArrayList",
                        [
                            {
                                "@class": "io.seata.rm.datasource.sql.struct.Row",
                                "fields": [
                                    "java.util.ArrayList",
                                    [
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "id",
                                            "keyType": "PRIMARY_KEY",
                                            "type": 4,
                                            "value": 2
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "available",
                                            "keyType": "NULL",
                                            "type": -7,
                                            "value": true
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                },
                "afterImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "coupon_template",
                    "rows": [
                        "java.util.ArrayList",
                        [
                            {
                                "@class": "io.seata.rm.datasource.sql.struct.Row",
                                "fields": [
                                    "java.util.ArrayList",
                                    [
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "id",
                                            "keyType": "PRIMARY_KEY",
                                            "type": 4,
                                            "value": 2
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "available",
                                            "keyType": "NULL",
                                            "type": -7,
                                            "value": false
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                }
            }
        ]
    ]
}

2.3:seata TCC模式

包含try,confirm,cancel,三个阶段:
在这里插入图片描述
其中各个阶段所作的事情如下:

try:锁定资源,相当于prepare
confirm:执行具体的操作,相当于commit
cancel:解锁try锁定的资源,相当于rollback

为了实现TCC,我们首先需要注册TCC接口:

@LocalTCC
public interface CouponTemplateServiceTCC extends CouponTemplateService {

    @TwoPhaseBusinessAction(
            name = "deleteTemplateTCC",
            commitMethod = "deleteTemplateCommit",
            rollbackMethod = "deleteTemplateCancel"
    )
    void deleteTemplateTCC(@BusinessActionContextParameter(paramName = "id") Long id);

    void deleteTemplateCommit(BusinessActionContext context);

    void deleteTemplateCancel(BusinessActionContext context);
}

为了支持try操作,在表中增加lock字段,如下:

alter table coupon_template
   add locked tinyint(1) default 0 null;

对应的实体也需要修改:

@Column(name = "locked", nullable = false)
private Boolean locked;

接着tcc三个阶段对应的方法如下:

// TCC 的T 通过修改lock字段值,完成锁定
@Override
@Transactional
public void deleteTemplateTCC(Long id) {
    CouponTemplate filter = CouponTemplate.builder()
            .available(true)
            .locked(false)
            .id(id)
            .build();

    CouponTemplate template = templateDao.findAll(Example.of(filter))
            .stream().findFirst()
            .orElseThrow(() -> new RuntimeException("Template Not Found"));

    template.setLocked(true);
    templateDao.save(template);
}

// TCC 的第一个C
@Override
@Transactional
public void deleteTemplateCommit(BusinessActionContext context) {
    Long id = Long.parseLong(context.getActionContext("id").toString());

    CouponTemplate template = templateDao.findById(id).get();

    template.setLocked(false);
    template.setAvailable(false);
    templateDao.save(template);

    log.info("TCC committed");
}

// TCC 的第二个C
@Override
@Transactional
public void deleteTemplateCancel(BusinessActionContext context) {
    Long id = Long.parseLong(context.getActionContext("id").toString());
    Optional<CouponTemplate> templateOption = templateDao.findById(id);
    // 空回滚
    if (templateOption.isPresent()) {
        CouponTemplate template = templateOption.get();
        // 通过修改lock值解锁
        template.setLocked(false);
        templateDao.save(template);
    }
    log.info("TCC cancel");
}

注意TCC,每个阶段都需要自己编码实现,如何try是这里最关键的,需要我们对业务有着深刻的理解,还需要考虑空回滚,悬挂等问题,所以使用的门槛还是比较高的。done!!!

写在后面

要不要在系统中使用分布式事务?非必要不要引入分布式事务,原因如下:

1:增加系统的复杂度,引入额外的组件,降低系统的可用性,增加系统维护的复杂程度
2:降低系统的性能

如果系统需要考虑分布式事务的话,还是尽量使用事务性消息和job补偿的方式来实现,这样对现有业务的影响是最小的。

参考文章列表

MySQL之分布式事务 。

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

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

相关文章

GIT使用,看它就够了

一、目的 Git的熟练使用是一个加分项&#xff0c;本文将对常用的Git命令做一个基本介绍&#xff0c;看了本篇文章&#xff0c;再也不会因为不会使用git而被嘲笑了。 二、设置与配置 在第一次调用Git到日常的微调和参考&#xff0c;用得最多的就是config和help命令。 2.1 gi…

腾讯云4核16G幻兽帕鲁服务器多少钱?

腾讯云幻兽帕鲁服务器4核16G14M配置&#xff0c;14M公网带宽&#xff0c;限制2500GB月流量&#xff0c;系统盘为220GB SSD盘&#xff0c;优惠价格66元1个月&#xff0c;277元3个月&#xff0c;支持4到8个玩家畅玩&#xff0c;地域可选择上海/北京/成都/南京/广州&#xff0c;腾…

机器学习和可视化还能一起这样用?Python教你全搞定

今天这篇推文&#xff0c;我们继续空间数据可视化的最后一个系列-类别插值(categorical-spatial-interpolation) 可视化绘制的推文教程&#xff0c;这期我们使用Python进行绘制&#xff0c;涉及的知识点如下&#xff1a; sklearn.KNeighborsClassifier()机器学习应用 plotnine…

4JS表达式和运算符expression and operator

表达式&#xff08;expression&#xff09;JavaScript中的一个短语&#xff0c;JavaScript解释器会将其计算&#xff08;evaluate&#xff09;出一个结果。程序中的常量是最简单的一类表达式。变量名也是一种简单的表达式&#xff0c;它的值就是赋值给变量的值。复杂表达式是由…

【第二天】蓝桥杯备战

题 1、星期一2、顺子日期3、修剪灌木4、最少砝码 1、星期一 https://www.lanqiao.cn/problems/611/learning/ 解法&#xff1a;星期一的天数总共的天数/7 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void ma…

AutoGen实战应用(二):多代理协作(Multi-Agent Collaboration)

AutoGen是微软推出的一个全新工具&#xff0c;它用来帮助开发者创建基于大语言模型(LLM)的复杂应用程序. AutoGen能让LLM在复杂工作流程启用多个角色代理来共同协作完成人类提出的任务。在我之前的一篇博客: AutoGen实战应用(一)&#xff1a;代码生成、执行和调试 中我们通过一…

Web3:B站chainlink课程Lesson5遇到的小坑汇总

ethers代码 我用的ethers.js 6 &#xff0c;和视频里一样用的是5的不用看代码部分 ethers.providers.JsonRpcProvider("server") //无了 ethers.JsonRpcProvider("server") //现在的wallet.getTransactionCount() //无了 wallet.getNonce() //现在的Big…

springboot的actuator

1、actuator简介 微服务的特点决定了功能模块的部署是分布式的&#xff0c;大部分功能模块都是运行在不同的机器上&#xff0c;彼此通过服务调用进行交互&#xff0c;前后台的业务流会经过很多个微服务的处理和传递&#xff0c;出现了异常如何快速定位是哪个环节出现了问题&am…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型、视觉语言导航

专属领域论文订阅 关注{晓理紫|小李子}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 分类: 大语言模型LLM视觉模型VLM扩散模型视觉…

字符串相关函数【超详细】(strcpy,strstr等string.h中的函数)

文章目录 strlen库中函数定义函数作用函数大概“工作”流程函数使用注意&#xff08;要求&#xff09;函数使用例举 strcpy库中函数定义函数作用函数使用注意&#xff08;要求&#xff09;函数大概“工作”流程函数使用例举 strcat库中函数定义函数作用函数使用注意&#xff08…

【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]

阅读导航 引言一、特殊类 --- 不能被拷贝的类1. C98方式&#xff1a;2. C11方式&#xff1a; 二、特殊类 --- 只能在堆上创建对象的类三、特殊类 --- 只能在栈上创建对象的类四、特殊类 --- 不能被继承的类1. C98方式2. C11方法 总结温馨提示 引言 在面向对象编程中&#xff0…

【计网·湖科大·思科】实验三 总线型以太网的特性、集线器和交换机的区别、交换机的自学习算法

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

kubeadm部署k8s1.27.2版本高可用集群(外部etcd集群带TLS认证)

文章目录 环境软件版本服务器系统初始化etcd 证书生成etcd集群部署负载均衡器部署部署k8s集群部署网络组件FAQ 环境 控制平面节点主机的配置最少是2C2G,否则kubeadm init的时候会报错 主机名IP组件系统os128192.168.177.128etcd、kubeadm、kube-apiserver、kube-controller-m…

Vue3 pinia全解(上)

pinia是什么&#xff1f; 如果你学过Vue2&#xff0c;那么你一定使用过Vuex。我们都知道Vuex在Vue2中主要充当状态管理的角色&#xff0c;所谓状态管理&#xff0c;简单来说就是一个存储数据的地方&#xff0c;存放在Vuex中的数据在各个组件中都能访问到&#xff0c;它是Vue生…

【学术论文写作 笔记02】 鲁棒性实验写作的行文逻辑

文章目录 一、声明二、行文思路三、示例范文一范文二 一、声明 自己总结的&#xff0c;有问题望指正&#xff01; 二、行文思路 为什么要做鲁棒性测试怎么做实验结论对结果的解释 三、示例 PPT 范文一 2022, TIM, “A Robust and Reliable Point Cloud Recognition Netw…

Java零基础学习22:static关键字

编写博客目的&#xff1a;本系列博客均根据B站黑马程序员系列视频学习和编写目的在于记录自己的学习点滴&#xff0c;方便后续回忆和查找相关知识点&#xff0c;不足之处恳请各位有缘的朋友指正。 一、static的初步应用场景 我们开始时不使用static直接用public赋值&#xff…

租户认证系统中心设计与实践

租⼾认证中⼼设计、需求背景 1. 由于客⼾&#xff0c;租⼾之间缺乏严肃的关系&#xff0c;导致从经营⻆度看不清客⼾对于公司产品的真实使⽤情况&#xff0c;对于客⼾运营也造成⼀定影响。 2. 在各个业务产品中实际存在⼀些客⼾需要完善⾃⾝资料的场景&#xff0c;但这些场景收…

上位机图像处理和嵌入式模块部署(c/c++ opencv)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 opencv可以运行在多个平台上面&#xff0c;当然windows平台也不意外。目前来说&#xff0c;opencv使用已经非常方便了&#xff0c;如果不想自己编译…

ANSYS 2023 下载安装教程,附安装包和工具,轻松安装,无套路

前言 ANSYS是一款融结构、流体、电场、磁场、声场分析于一体的大型通用有限元分析(FEA)软件&#xff0c;能与多数计算机辅助设计软件接口&#xff0c;实现数据的共享和交换&#xff0c;如Creo,NASTRAN、Algor、IDEAS、AutoCAD等. 准备工作 1、Win10及以上系统 2、提前准备好…

Keepalived 深度解析:高可用性的精髓及实践指南

Keepalived 深度解析&#xff1a;高可用性的精髓及实践指南 Keepalived 深度解析&#xff1a;高可用性的精髓及实践指南Keepalived 的工作原理1. VRRP 协议概述2. Keepalived 的角色3. VRRP 协议详解 Keepalived 的使用指南1. 安装 Keepalived使用 Yum 安装本地安装方式 2. 配置…