栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

微服务分布式事务(微服务概念)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

微服务分布式事务(微服务概念)

什么是分布式事务

单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,
业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
说白了就是一次调用需要修改不同服务器上的数据库,导致一个服务器崩了数据没有按照原有的预期的方式执行,导致数据错误的问题。

Seata简介 是什么

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

官网地址

能干嘛 组成

由一个全局唯一事务id以及三个组件组成分别是:

    全局唯一的事务idTransaction Coordinator (TC):事务提交回滚的头头,事务最终是由它管理的Transaction Manager ™:确定事务边界,负责管理协调这个事务的范围Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分支(本地)事务的提交和回滚
处理过程
    TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;XID 在微服务调用链路的上下文中传播;RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;TM 向 TC 发起针对 XID 的全局提交或回滚决议;TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

如何使用 步骤 下载

注意笔者下载的是seata-server-0.9.0.zip
https://github.com/seata/seata/releases

配置

打开seata-server的conf下的file.conf

1.自定义一个事务组名称

service {
  #vgroup->rgroup
  vgroup_mapping.my_test_tx_group = "fsp_tx_group"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}
    指向自己的数据库

db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "root"
    password = "123456"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
    配置registry.conf将这个组件注册到nacos中

 type = "nacos"

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
数据库配置
    在自己所使用的数据库中创建一个seata的数据库将seata中的sql脚本刷到这个库中
启动nacos 启动Seata

如下图所示,如果服务列表出现名为serverAddr的服务就说明部署成功了

订单/库存/账户业务数据库准备 业务说明

这里我们会创建三个服务,一个订单服务,一个库存服务,一个账户服务。

当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,
再通过远程调用账户服务来扣减用户账户里面的余额,
最后在订单服务中修改订单状态为已完成。

该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。

建库
CREATE DATAbase seata_order;
  
CREATE TABLE t_order (
  `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
  `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
  `count` INT(11) DEFAULT NULL COMMENT '数量',
  `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
  `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结' 
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
 
SELECt * FROM t_order;
CREATE DATAbase seata_storage;

 
CREATE TABLE t_storage (
 `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
 `total` INT(11) DEFAULT NULL COMMENT '总库存',
 `used` INT(11) DEFAULT NULL COMMENT '已用库存',
 `residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
 
INSERT INTO seata_storage.t_storage(`id`, `product_id`, `total`, `used`, `residue`)
VALUES ('1', '1', '100', '0', '100');
 
SELECt * FROM t_storage;
CREATE DATAbase seata_account;
 
CREATE TABLE t_account (
  `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
  `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
  `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
  `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',
  `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
INSERT INTO seata_account.t_account(`id`, `user_id`, `total`, `used`, `residue`)  VALUES ('1', '1', '1000', '0', '1000');
 
SELECt * FROM t_account;
订单-库存-账户3个库下都需要建各自的回滚日志表

在每个数据库中跑下图文件的sql建表脚本即可

订单模块搭建 创建模块

new 一个module名为seata-order-service2001

修改pom

注意seata版本需要和当前使用版本一致



    
        mscloud
        com.zsy.springcloud
        1.0-SNAPSHOT
    
    4.0.0

    com.zsy.springcloud
    seata-order-service2001
    1.0-SNAPSHOT



    
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-seata
            
                
                    seata-all
                    io.seata
                
            
        
        
            io.seata
            seata-all
            0.9.0
        
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
        
            mysql
            mysql-connector-java
            5.1.37
        
        
            com.alibaba
            druid-spring-boot-starter
            1.1.10
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.0
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.projectlombok
            lombok
            true
        
    



添加yml

注意事务组名称需要和上文seata中配置的一致

server:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order
    username: root
    password: 123456

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper
    private Integer status;
}
dao、service、controller

注意这里库存和账户余额扣款都是通过feign来进行调用的

创建相应的库存服务和扣款服务

库存服务


扣款服务

具体可以参照笔者源码

https://gitee.com/fugongliudehua/mscloud

测试

我们想测试分布式事务,且我们都知道feign超时时间是1s,所以我们可以尝试在账户扣款中增加一个线程休眠,如下图所示

如果去掉下图所示注解,我们可以看到t_order表创建了订单,并且storage表扣了库存,而用户钱包即t_account没有扣款,很明显这种情况等于让用户白嫖了

反之我们将注解接触就会发现,扣款服务feign报超时异常时,所有数据都会自动回滚,保证了分布式事务

seata工作原理 前置步骤

我们不妨在订单service类中添加如下所示断点

一阶段提交

在一阶段,Seata 会拦截“业务 SQL”,

    解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,执行“业务 SQL”更新业务数据,在业务数据更新之后,其保存成“after image”,最后生成行锁。
    以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

    所以我们代码debug到库存服务的feign调用时


可以看到undo log表插入了一条数据,这就是所谓的RM 向 TC 汇报资源准备状态


并且我们可以看到rollback info中,记录了扣款前和扣款后的值



当我们执行到如下代码,由于我们设置了线程休眠30s,触发超时异常,触发了二阶段回滚

二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。
回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,
如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

可以看到undo log之前的数据没了


反之二阶段如是顺利提交的话,
因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

源码地址

https://gitee.com/fugongliudehua/mscloud

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/773453.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号