spring cloud之分布式事务

写在前面

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");
}

done!!!

写在后面

参考文章列表

MySQL之分布式事务 。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

webassembly003 whisper.cpp的main项目-1

参数设置 /home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug/bin/main options:-h, --help [default] show this help message and exit-t N, --threads N [4 ] number of threads to use during computation-p N, --processors …

使用css将文字在水平线中显示

方法一&#xff1a; 1.效果图 2.html <!-- <div class"line">第三方登录</div> --> 3.css /* 让文字在水平线中显示 */.line {display: flex;flex-direction: row;color: #ccc;font-size: 18px;font-weight: bolder; }.line:before, .line:aft…

Servlet API

Servlet的API就是一组类和方法 其中主要的三个类有 HttpServlet HttpServletRequest HttpServletResponse HttpServlet 这是编写Servlet代码用到的核心类 通过继承这个类 并重新写其中的方法 让tomcat去调用到这里的逻辑 方法名称调用时机init在HttpServlet实例化之后被调用…

攻防世界WEB新手训练区

view_source 此题我愿称之为网安领域的hello world 查看网页源代码的方式一般有—— 右键->查看网页源代码F12->源代码/来源Ctrlu 随后可以再代码第17行处找到flag&#xff0c;至此迈入网安第一步。可喜可贺&#xff0c;可喜可贺... get_post 考察http的两种请求方式&…

【大数据面试题】HBase面试题附答案

目录 1.介绍下HBase 2.HBase优缺点 3.介绍下的HBase的架构 4.HBase的读写缓存 5.在删除HBase中的一个数据的时候&#xff0c;它是立马就把数据删除掉了吗? 6.HBase中的二级索引 7.HBase的RegionServer宕机以后怎么恢复的? 8.HBase的一个region由哪些东西组成? 9.…

论述Python中列表、元组、字典和集合的概念

Python列表是用于存储任意数目、任意类型的数据集合&#xff0c;包含多个元素的有序连续的内存空间&#xff0c;是内置可变序列&#xff0c;或者说可以任意修改。在Python中&#xff0c;列表以方括号&#xff08;[ ]&#xff09;形式编写。 Python元组与Python列表类似&#x…

oak-d-lite摄像头 去噪处理

前言 试过了各种滤波才知道&#xff0c;为啥oak在示例算法只是使用5X5滤波而不使用更好的滤波的原因了。 实验内容 oak-d-lite使用的工业相机噪声主要表现为随机的亮度变化&#xff0c;使用Non-Local Means Denoising算法后去噪效果很好&#xff08;左为原图&#xff09;&…

3D建模素材网站的特点有哪些?

3D建模素材网站的特点主要包括丰富多样的模型种类、高质量的模型、实时预览功能、易于使用、价格合理以及社区互动等。这些特点使得3D建模素材网站成为设计师们不可或缺的资源之一&#xff0c;帮助他们快速高效地完成设计工作。 那么3D建模素材网站的特点有哪些? 1、模型种类丰…

【复现】Laykefu客服系统后台漏洞合集_29

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 2. 漏洞二&#xff1a; 3. 漏洞三&#xff1a; 4. 漏洞四&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Laykefu客服系统是thinkphp5Gatewayworker搭建的web客服…

「QT」QString类的详细说明

✨博客主页何曾参静谧的博客📌文章专栏「QT」QT5程序设计📚全部专栏「VS」Visual Studio「C/C++」C/C++程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「

契约锁高校行业电子签应用报告

高校数字化建设起步早&#xff0c;也是电子签章应用最为广泛的行业之一。为了及时为高校组织提供服务参考&#xff0c;契约锁整合了近年在高校行业的方案、场景、案例等资源生成“高校电子签应用报告”&#xff0c;随时为数字化校园建设助力。 高校电子签建设所需资料都在这 整…

ps---1.使用置入嵌入的智能化对象命令制作拼贴画

ps—使用置入嵌入的智能化对象命令制作拼贴画 素材 步骤 打开背景图 置入嵌入对象&#xff08;置入完&#xff0c;移动到正确的位置&#xff0c;回车或双击&#xff09; 栅格化图层 重复上述步骤制作拼贴画

图像读写(imgcodecs 模块)与视频读写

内置格式 OpenCV 可以在没有任何第三方库帮助的情况下读取以下格式&#xff1a; BMP的 高动态范围 (WITH_IMGCODEC_HDR) 太阳栅格 (WITH_IMGCODEC_SUNRASTER) PPM、PGM、PBM、PFM&#xff08;、WITH_IMGCODEC_PXMWITH_IMGCODEC_PFM) PNG、JPEG、TIFF、WEBP 支持 格式选择…

[SUCTF 2019]CheckIn1

黑名单过滤后缀’ph&#xff0c;并且白名单image类型要有对应文件头 对<?过滤&#xff0c;改用GIF89a<script languagephp>eval($_POST[cmd]);</script>&#xff0c;成功把getshell.gif上传上去了 尝试用.htaccess将上传的gif当作php解析&#xff0c;但是失败…

Flask 之旅 (二):表单

背景 上一篇帖子我们使用 Flask 创建了最基本的 web 服务。使用 bootstrap 对页面进行装点&#xff0c;使用 JQuery Ajax 实现了在页面上实时显示 log 的功能。趁着周末&#xff0c;我继续开始学习更多的东西以满足这个 web 服务的需求。 模板继承 之前我们有了首页&#xf…

多线程-单例模式

目录 1.单例模式 举例&#xff1a; 1.1单例模式的实现 饿汉模式&#xff1a; 懒汉模式&#xff1a; 1.单例模式 单例模式是一种设计模式。单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例. 举例&#xff1a; package 多线程;import javax.manage…

Django从入门到精通(三)

目录 七、ORM操作 7.1、表结构 常见字段 参数 示例 7.2、表关系 一对多 多对多 第一种方式 第二种方式 7.3、连接MYSQL 7.4、数据库连接池 7.5、多数据库 读写分离 分库&#xff08;多个app ->多数据库&#xff09; 分库&#xff08;单app&#xff09; 注意…

微服务入门篇:Eureka注册中心(作用,搭建Eureka客户端和服务端)

目录 1.提供者与消费者2.Eureka的作用3.搭建EurekaServer1.配置服务端2.配置客户端3.复制实例操作4.服务拉取 1.提供者与消费者 ①服务提供者:一次业务中&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务) ②服务消费者:一次业务中&#xff0c;调用其它微…

【机器学习】全网最全模型评价指标(性能指标、YOLOv5训练结果分析、轻量化指标、混淆矩阵详解)【基础收藏】

&#x1f951; Welcome to Aedream同学 s blog! &#x1f951; 文章目录 模型性能指标常见指标ROC/AUCROC & PRC多分类问题——混淆矩阵 计算结果分析——以YOLO v5为例1. confusion_matrix.png(混淆矩阵)2. F1_curve&#xff1a;3. labels.jpg4. labels_corrrelogram.jpg5…

【unity实战】实现蓄力丢手榴弹、烟雾弹、燃烧弹的效果

文章目录 爆炸燃烧烟雾效果资产手榴弹丢手雷烟雾弹、燃烧弹实现手雷每次撞墙弹发出音效&#xff08;补充&#xff09;完结 爆炸燃烧烟雾效果资产 https://assetstore.unity.com/packages/vfx/particles/war-fx-5669 手榴弹 手榴弹配置好刚体&#xff0c;碰撞体 新增脚本Th…
最新文章