Seata 案例

桃扇骨 2024-03-23 23:32 230阅读 0赞

分布式事务问题由来

分布式前

在这里插入图片描述

分布式后

在这里插入图片描述
在这里插入图片描述

Seata术语

官网地址: http://seata.io/zh-cn/

是什么

在这里插入图片描述

能干嘛

在这里插入图片描述
分布式事务处理过程的一ID+三组件模型:

  • Transaction ID XID 全局唯一的事务ID
  • 三组件概念

    • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
    • TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
    • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

处理过程:

  1. TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;
  2. XID在微服务调用链路的上下文中传播;
  3. RM向TC注册分支事务,将其纳入XID对应全局事务的管辖;
  4. TM向TC发起针对XID的全局提交或回滚决议;
  5. TC调度XID下管辖的全部分支事务完成提交或回滚请求。

在这里插入图片描述

Seata-Server安装

去哪下

发布说明: https://github.com/seata/seata/releases

怎么玩

在这里插入图片描述
在这里插入图片描述

Seata-Server安装

在这里插入图片描述

下载地址:

https://github.com/seata/seata/releases

https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip

seata-server-1.4.2.zip解压到指定目录并修改conf目录下的 file.conf 配置文件

先备份原始file.conf文件

在这里插入图片描述

主要修改:自定义事务组名称+事务日志存储模式为db +数据库连接信息

在这里插入图片描述

打开 file.conf.example 文件

在这里插入图片描述

  1. #新增
  2. #vgroup->rgroup
  3. ##fsp_tx_group是自定义的
  4. vgroup_mapping.my.test.tx_group="fsp_tx_group"

在这里插入图片描述

mysql8.0.34 数据库新建库seata,在seata库里建表

在seata库里建表

注意:1.4.2版本的seata文件下没有db.sql的脚本文件,需要去seata的GitHub上下载
路径:https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql

  1. -- -------------------------------- The script used when storeMode is 'db' --------------------------------
  2. -- the table to store GlobalSession data
  3. CREATE TABLE IF NOT EXISTS `global_table`
  4. (
  5. `xid` VARCHAR(128) NOT NULL,
  6. `transaction_id` BIGINT,
  7. `status` TINYINT NOT NULL,
  8. `application_id` VARCHAR(32),
  9. `transaction_service_group` VARCHAR(32),
  10. `transaction_name` VARCHAR(128),
  11. `timeout` INT,
  12. `begin_time` BIGINT,
  13. `application_data` VARCHAR(2000),
  14. `gmt_create` DATETIME,
  15. `gmt_modified` DATETIME,
  16. PRIMARY KEY (`xid`),
  17. KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
  18. KEY `idx_transaction_id` (`transaction_id`)
  19. ) ENGINE = InnoDB
  20. DEFAULT CHARSET = utf8mb4;
  21. -- the table to store BranchSession data
  22. CREATE TABLE IF NOT EXISTS `branch_table`
  23. (
  24. `branch_id` BIGINT NOT NULL,
  25. `xid` VARCHAR(128) NOT NULL,
  26. `transaction_id` BIGINT,
  27. `resource_group_id` VARCHAR(32),
  28. `resource_id` VARCHAR(256),
  29. `branch_type` VARCHAR(8),
  30. `status` TINYINT,
  31. `client_id` VARCHAR(64),
  32. `application_data` VARCHAR(2000),
  33. `gmt_create` DATETIME(6),
  34. `gmt_modified` DATETIME(6),
  35. PRIMARY KEY (`branch_id`),
  36. KEY `idx_xid` (`xid`)
  37. ) ENGINE = InnoDB
  38. DEFAULT CHARSET = utf8mb4;
  39. -- the table to store lock data
  40. CREATE TABLE IF NOT EXISTS `lock_table`
  41. (
  42. `row_key` VARCHAR(128) NOT NULL,
  43. `xid` VARCHAR(128),
  44. `transaction_id` BIGINT,
  45. `branch_id` BIGINT NOT NULL,
  46. `resource_id` VARCHAR(256),
  47. `table_name` VARCHAR(32),
  48. `pk` VARCHAR(36),
  49. `status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
  50. `gmt_create` DATETIME,
  51. `gmt_modified` DATETIME,
  52. PRIMARY KEY (`row_key`),
  53. KEY `idx_status` (`status`),
  54. KEY `idx_branch_id` (`branch_id`),
  55. KEY `idx_xid` (`xid`)
  56. ) ENGINE = InnoDB
  57. DEFAULT CHARSET = utf8mb4;
  58. CREATE TABLE IF NOT EXISTS `distributed_lock`
  59. (
  60. `lock_key` CHAR(20) NOT NULL,
  61. `lock_value` VARCHAR(20) NOT NULL,
  62. `expire` BIGINT,
  63. primary key (`lock_key`)
  64. ) ENGINE = InnoDB
  65. DEFAULT CHARSET = utf8mb4;
  66. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
  67. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
  68. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
  69. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

修改seata-server-1.4.2\seata\conf目录下的registry.conf配置文件

先备份副本

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  3. type = "nacos"
  4. nacos {
  5. application = "seata-server"
  6. serverAddr = "127.0.0.1:8848"
  7. group = "SEATA_GROUP"
  8. namespace = ""
  9. cluster = "default"
  10. username = ""
  11. password = ""
  12. }

先启动Nacos端口号8848 nacos\bin\startup.cmd

再启动seata-server - seata-server-1.4.2\seata\bin\seata-server.bat

直接双击 seata-server-1.4.2\seata\bin\ 目录下的 seata-server.bat 即可启动 seata

Seata业务数据库准备

以下演示都需要先启动Nacos后启动Seata,保证两个都OK

在这里插入图片描述

分布式事务业务说明

在这里插入图片描述
在这里插入图片描述

创建业务数据库

  • seata_ order:存储订单的数据库
  • seata_ storage:存储库存的数据库
  • seata_ account:存储账户信息的数据库

建库SQL

  1. CREATE DATABASE seata_order;
  2. CREATE DATABASE seata_storage;
  3. CREATE DATABASE seata_account;

按照上述3库分别建对应业务表

  • seata_order库下建t_order表

    CREATE TABLE t_order (

    1. `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    2. `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    3. `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    4. `count` INT(11) DEFAULT NULL COMMENT '数量',
    5. `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
    6. `status` INT(1) DEFAULT NULL COMMENT '订单状态: 0:创建中; 1:已完结'

    ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

    SELECT * FROM t_order;

  • seata_storage库下建t_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=1 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;

  • seata_account库下建t_account表

    CREATE TABLE t_account(

    1. `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
    2. `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    3. `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
    4. `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',
    5. `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度'

    ) ENGINE=INNODB AUTO_INCREMENT=1 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库分别建对应的回滚日志表

  • 订单-库存-账户3个库下都需要建各自的回滚日志表
  • 建表SQL

    — the table to store seata xid data
    — 0.7.0+ add context
    — you must to init this sql for you business databese. the seata server not need it.
    — 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
    — 注意此处0.3.0+ 增加唯一索引 ux_undo_log
    drop table undo_log;
    CREATE TABLE undo_log (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    branch_id bigint(20) NOT NULL,
    xid varchar(100) NOT NULL,
    context varchar(128) NOT NULL,
    rollback_info longblob NOT NULL,
    log_status int(11) NOT NULL,
    log_created datetime NOT NULL,
    log_modified datetime NOT NULL,
    ext varchar(100) DEFAULT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY ux_undo_log (xid,branch_id)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

在这里插入图片描述

Seata之Order-Module配置搭建

业务需求

在这里插入图片描述

新建订单 Order-Module:seata-order-service2001

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>cloud2020</artifactId>
  7. <groupId>com.atguigu.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>seata-order-service2001</artifactId>
  12. <dependencies>
  13. <!--nacos-->
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  17. </dependency>
  18. <!--seata-->
  19. <dependency>
  20. <groupId>com.alibaba.cloud</groupId>
  21. <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  22. <exclusions>
  23. <exclusion>
  24. <artifactId>seata-all</artifactId>
  25. <groupId>io.seata</groupId>
  26. </exclusion>
  27. </exclusions>
  28. </dependency>
  29. <dependency>
  30. <groupId>io.seata</groupId>
  31. <artifactId>seata-all</artifactId>
  32. <version>1.4.2</version>
  33. </dependency>
  34. <!--feign-->
  35. <dependency>
  36. <groupId>org.springframework.cloud</groupId>
  37. <artifactId>spring-cloud-starter-openfeign</artifactId>
  38. </dependency>
  39. <!--web-actuator-->
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-starter-web</artifactId>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-actuator</artifactId>
  47. </dependency>
  48. <!--mysql-druid-->
  49. <dependency>
  50. <groupId>mysql</groupId>
  51. <artifactId>mysql-connector-java</artifactId>
  52. <version>5.1.6</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>com.alibaba</groupId>
  56. <artifactId>druid-spring-boot-starter</artifactId>
  57. <version>1.1.10</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.mybatis.spring.boot</groupId>
  61. <artifactId>mybatis-spring-boot-starter</artifactId>
  62. <version>2.0.0</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.springframework.boot</groupId>
  66. <artifactId>spring-boot-starter-test</artifactId>
  67. <scope>test</scope>
  68. </dependency>
  69. <dependency>
  70. <groupId>org.projectlombok</groupId>
  71. <artifactId>lombok</artifactId>
  72. <optional>true</optional>
  73. </dependency>
  74. </dependencies>
  75. </project>

application.yml

  1. server:
  2. port: 2001
  3. spring:
  4. application:
  5. name: seata-order-service
  6. cloud:
  7. alibaba:
  8. seata:
  9. #自定义事务组名称需要与seata-server中的对应
  10. tx-service-group: fsp_tx_group
  11. nacos:
  12. discovery:
  13. server-addr: localhost:8848
  14. datasource:
  15. driver-class-name: com.mysql.jdbc.Driver
  16. url: jdbc:mysql://localhost:3306/seata_order
  17. username: root
  18. password: 123456
  19. feign:
  20. hystrix:
  21. enabled: false
  22. logging:
  23. level:
  24. io:
  25. seata: info
  26. mybatis:
  27. mapperLocations: classpath:mapper/*.xml

在 resources 目录下新建 file.conf 文件, 将 seata 目录下的 conf/file.conf 复制进来 ,修改对应的数据库地址,数据库名,用户名,密码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. transport {
  2. # tcp, unix-domain-socket
  3. type = "TCP"
  4. #NIO, NATIVE
  5. server = "NIO"
  6. #enable heartbeat
  7. heartbeat = true
  8. # the client batch send request enable
  9. enableClientBatchSendRequest = false
  10. #thread factory for netty
  11. threadFactory {
  12. bossThreadPrefix = "NettyBoss"
  13. workerThreadPrefix = "NettyServerNIOWorker"
  14. serverExecutorThreadPrefix = "NettyServerBizHandler"
  15. shareBossWorker = false
  16. clientSelectorThreadPrefix = "NettyClientSelector"
  17. clientSelectorThreadSize = 1
  18. clientWorkerThreadPrefix = "NettyClientWorkerThread"
  19. # netty boss thread size
  20. bossThreadSize = 1
  21. #auto default pin or 8
  22. workerThreadSize = "default"
  23. }
  24. shutdown {
  25. # when destroy server, wait seconds
  26. wait = 3
  27. }
  28. serialization = "seata"
  29. compressor = "none"
  30. }
  31. ## transaction log store, only used in server side
  32. store {
  33. ## store mode: file、db
  34. mode = "db"
  35. ## file store property
  36. file {
  37. ## store location dir
  38. dir = "sessionStore"
  39. # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
  40. maxBranchSessionSize = 16384
  41. # globe session size , if exceeded throws exceptions
  42. maxGlobalSessionSize = 512
  43. # file buffer size , if exceeded allocate new buffer
  44. fileWriteBufferCacheSize = 16384
  45. # when recover batch read size
  46. sessionReloadReadSize = 100
  47. # async, sync
  48. flushDiskMode = async
  49. }
  50. ## database store property
  51. db {
  52. ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
  53. datasource = "druid"
  54. ## mysql/oracle/postgresql/h2/oceanbase etc.
  55. dbType = "mysql"
  56. driverClassName = "com.mysql.jdbc.Driver"
  57. ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
  58. url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
  59. user = "root"
  60. password = "123456"
  61. minConn = 5
  62. maxConn = 30
  63. globalTable = "global_table"
  64. branchTable = "branch_table"
  65. lockTable = "lock_table"
  66. queryLimit = 100
  67. }
  68. }
  69. ## server configuration, only used in server side
  70. server {
  71. recovery {
  72. #schedule committing retry period in milliseconds
  73. committingRetryPeriod = 1000
  74. #schedule asyn committing retry period in milliseconds
  75. asynCommittingRetryPeriod = 1000
  76. #schedule rollbacking retry period in milliseconds
  77. rollbackingRetryPeriod = 1000
  78. #schedule timeout retry period in milliseconds
  79. timeoutRetryPeriod = 1000
  80. }
  81. undo {
  82. logSaveDays = 7
  83. #schedule delete expired undo_log in milliseconds
  84. logDeletePeriod = 86400000
  85. }
  86. #check auth
  87. enableCheckAuth = true
  88. #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  89. maxCommitRetryTimeout = "-1"
  90. maxRollbackRetryTimeout = "-1"
  91. rollbackRetryTimeoutUnlockEnable = false
  92. retryDeadThreshold = 130000
  93. #新增
  94. #vgroup->rgroup
  95. ##fsp_tx_group是自定义的
  96. vgroup_mapping.my.test.tx_group="fsp_tx_group"
  97. }
  98. ## metrics configuration, only used in server side
  99. metrics {
  100. enabled = false
  101. registryType = "compact"
  102. # multi exporters use comma divided
  103. exporterList = "prometheus"
  104. exporterPrometheusPort = 9898
  105. }

在 resources 目录下新建 registry.conf文件, 将 seata 目录下的 conf/registry.conf 复制进来

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  3. type = "nacos"
  4. nacos {
  5. application = "seata-server"
  6. serverAddr = "127.0.0.1:8848"
  7. group = "SEATA_GROUP"
  8. namespace = ""
  9. cluster = "default"
  10. username = ""
  11. password = ""
  12. }
  13. eureka {
  14. serviceUrl = "http://localhost:8761/eureka"
  15. application = "default"
  16. weight = "1"
  17. }
  18. redis {
  19. serverAddr = "localhost:6379"
  20. db = 0
  21. password = ""
  22. cluster = "default"
  23. timeout = 0
  24. }
  25. zk {
  26. cluster = "default"
  27. serverAddr = "127.0.0.1:2181"
  28. sessionTimeout = 6000
  29. connectTimeout = 2000
  30. username = ""
  31. password = ""
  32. }
  33. consul {
  34. cluster = "default"
  35. serverAddr = "127.0.0.1:8500"
  36. aclToken = ""
  37. }
  38. etcd3 {
  39. cluster = "default"
  40. serverAddr = "http://localhost:2379"
  41. }
  42. sofa {
  43. serverAddr = "127.0.0.1:9603"
  44. application = "default"
  45. region = "DEFAULT_ZONE"
  46. datacenter = "DefaultDataCenter"
  47. cluster = "default"
  48. group = "SEATA_GROUP"
  49. addressWaitTime = "3000"
  50. }
  51. file {
  52. name = "file.conf"
  53. }
  54. }
  55. config {
  56. # file、nacos 、apollo、zk、consul、etcd3
  57. type = "nacos"
  58. nacos {
  59. serverAddr = "127.0.0.1:8848"
  60. namespace = ""
  61. group = "SEATA_GROUP"
  62. username = ""
  63. password = ""
  64. dataId = "seataServer.properties"
  65. }
  66. consul {
  67. serverAddr = "127.0.0.1:8500"
  68. aclToken = ""
  69. }
  70. apollo {
  71. appId = "seata-server"
  72. ## apolloConfigService will cover apolloMeta
  73. apolloMeta = "http://192.168.1.204:8801"
  74. apolloConfigService = "http://192.168.1.204:8080"
  75. namespace = "application"
  76. apolloAccesskeySecret = ""
  77. cluster = "seata"
  78. }
  79. zk {
  80. serverAddr = "127.0.0.1:2181"
  81. sessionTimeout = 6000
  82. connectTimeout = 2000
  83. username = ""
  84. password = ""
  85. nodePath = "/seata/seata.properties"
  86. }
  87. etcd3 {
  88. serverAddr = "http://localhost:2379"
  89. }
  90. file {
  91. name = "file.conf"
  92. }
  93. }

创建类 entity.CommonResult

  1. package com.atguigu.springcloud.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class CommonResult<T>
  9. {
  10. private Integer code;
  11. private String message;
  12. private T data;
  13. public CommonResult(Integer code, String message)
  14. {
  15. this(code,message,null);
  16. }
  17. }

创建类 entity.Order

  1. package com.atguigu.springcloud.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.math.BigDecimal;
  6. @Data
  7. @AllArgsConstructor
  8. @NoArgsConstructor
  9. public class Order
  10. {
  11. private Long id;
  12. private Long userId;
  13. private Long productId;
  14. private Integer count;
  15. private BigDecimal money;
  16. private Integer status; //订单状态:0:创建中;1:已完结
  17. }

Seata之Order-Module撸码(上)

Mapper接口及实现

mapper.OrderMapper

  1. package com.atguigu.springcloud.mapper;
  2. import com.atguigu.springcloud.entity.Order;
  3. import org.apache.ibatis.annotations.Param;
  4. public interface OrderMapper {
  5. // 新建订单
  6. void create(Order order);
  7. // 修改订单状态, 从 0 改为 1
  8. void update(@Param("userId") Long userId, @Param("status") Integer status);
  9. }

mapper.OrderMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.atguigu.springcloud.mapper.OrderMapper">
  4. <!-- 新增订单 -->
  5. <!-- void create(Order order); -->
  6. <insert id="create" parameterType="com.atguigu.springcloud.entity.Order">
  7. insert into seata_order.t_order(id, user_id, product_id, count, money, status)
  8. values (null,#{userId},#{productId},#{count},#{money},0);
  9. </insert>
  10. <!-- 修改订单状态 -->
  11. <!-- void update(@Param("userId") Long userId, @Param("status") Integer status); -->
  12. <update id="update">
  13. update seata_order.t_order set status = 1
  14. where user_id = #{userId} and status = #{status};
  15. </update>
  16. <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entity.Order">
  17. <id column="id" property="id" jdbcType="BIGINT"/>
  18. <result column="user_id" property="userId" jdbcType="BIGINT"/>
  19. <result column="product_id" property="productId" jdbcType="BIGINT"/>
  20. <result column="count" property="count" jdbcType="INTEGER"/>
  21. <result column="money" property="money" jdbcType="DECIMAL"/>
  22. <result column="status" property="status" jdbcType="INTEGER"/>
  23. </resultMap>
  24. </mapper>

Service接口及实现

  • StorageService
  • AccountService
  • OrderService

    • OrderServiceImpl

StorageService

  1. package com.atguigu.springcloud.service;
  2. import com.atguigu.springcloud.entity.CommonResult;
  3. import org.springframework.cloud.openfeign.FeignClient;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. @FeignClient(value = "seata-storage-service")
  7. public interface StorageService {
  8. @PostMapping("/storage/decrease")
  9. CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
  10. }

AccountService

  1. package com.atguigu.springcloud.service;
  2. import com.atguigu.springcloud.entity.CommonResult;
  3. import org.springframework.cloud.openfeign.FeignClient;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. import java.math.BigDecimal;
  7. @FeignClient(value = "seata-account-service")
  8. public interface AccountService {
  9. @PostMapping("/account/decrease")
  10. CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
  11. }

OrderService

  1. package com.atguigu.springcloud.service;
  2. import com.atguigu.springcloud.entity.Order;
  3. public interface OrderService {
  4. // 新建订单
  5. void create(Order order);
  6. }

OrderServiceImpl

  1. package com.atguigu.springcloud.service.impl;
  2. import com.atguigu.springcloud.entity.Order;
  3. import com.atguigu.springcloud.mapper.OrderMapper;
  4. import com.atguigu.springcloud.service.AccountService;
  5. import com.atguigu.springcloud.service.OrderService;
  6. import com.atguigu.springcloud.service.StorageService;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.stereotype.Service;
  9. import javax.annotation.Resource;
  10. @Service
  11. @Slf4j
  12. public class OrderServiceImpl implements OrderService {
  13. @Resource
  14. private OrderMapper orderMapper;
  15. @Resource
  16. private StorageService storageService;
  17. @Resource
  18. private AccountService accountService;
  19. /**
  20. * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
  21. * 简单说:下订单->扣库存->减余额->改状态
  22. */
  23. @Override
  24. public void create(Order order) {
  25. log.info("----->开始新建订单");
  26. // 1. 新建订单
  27. orderMapper.create(order);
  28. // 2. 扣减库存
  29. log.info("----->订单微服务开始调用库存,做扣减Count");
  30. storageService.decrease(order.getProductId(), order.getCount());
  31. log.info("----->订单微服务开始调用库存,做扣减end");
  32. // 3. 扣除账户余额
  33. log.info("----->订单微服务开始调用账户,做扣减Money");
  34. accountService.decrease(order.getUserId(), order.getMoney());
  35. log.info("----->订单微服务开始调用账户,做扣减end");
  36. // 4. 修改订单状态 从0到1,1代表已经完成
  37. log.info("----->修改订单状态开始");
  38. orderMapper.update(order.getUserId(), 0);
  39. log.info("----->修改订单状态结束");
  40. log.info("----->下订单结束了,O(∩_∩)O哈哈~");
  41. }
  42. }

Seata之Order-Module撸码(下)

controller.OrderController

  1. package com.atguigu.springcloud.controller;
  2. import com.atguigu.springcloud.entity.CommonResult;
  3. import com.atguigu.springcloud.entity.Order;
  4. import com.atguigu.springcloud.service.OrderService;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.annotation.Resource;
  8. @RestController
  9. public class OrderController {
  10. @Resource
  11. private OrderService orderService;
  12. @GetMapping("/order/create")
  13. public CommonResult create(Order order) {
  14. orderService.create(order);
  15. return new CommonResult(200,"订单创建成功");
  16. }
  17. }

Config配置

config.MyBatisConfig

  1. package com.atguigu.springcloud.config;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @MapperScan("com.atguigu.springcloud.mapper")
  6. public class MyBatisConfig {
  7. }

config.DataSourceProxyConfig

  1. package com.atguigu.springcloud.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import io.seata.rm.datasource.DataSourceProxy;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.mybatis.spring.SqlSessionFactoryBean;
  6. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.boot.context.properties.ConfigurationProperties;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  12. import javax.sql.DataSource;
  13. /**
  14. * 使用Seata对数据源进行代理
  15. */
  16. @Configuration
  17. public class DataSourceProxyConfig {
  18. @Value("${mybatis.mapperLocations}")
  19. private String mapperLocations;
  20. @Bean
  21. @ConfigurationProperties(prefix = "spring.datasource")
  22. public DataSource druidDataSource(){
  23. return new DruidDataSource();
  24. }
  25. @Bean
  26. public DataSourceProxy dataSourceProxy(DataSource dataSource) {
  27. return new DataSourceProxy(dataSource);
  28. }
  29. @Bean
  30. public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
  31. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  32. sqlSessionFactoryBean.setDataSource(dataSourceProxy);
  33. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
  34. sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
  35. return sqlSessionFactoryBean.getObject();
  36. }
  37. }

主启动类 SeataOrderMainApp2001

  1. package com.atguigu.springcloud;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.openfeign.EnableFeignClients;
  7. @EnableDiscoveryClient
  8. @EnableFeignClients
  9. //取消数据源的自动创建,而是使用自己定义的
  10. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
  11. public class SeataOrderMainApp2001 {
  12. public static void main(String[] args) {
  13. SpringApplication.run(SeataOrderMainApp2001.class, args);
  14. }
  15. }

启动微服务项目 2001

在这里插入图片描述

注意:启动微服务2001项目报以下错误

  1. can not get cluster name in registry config 'service.vgroupMapping.fsp_tx_group', please make sure registry config correct

解决方案:

在nacos中配置 service.vgroupMapping.fsp_tx_group

在这里插入图片描述
在这里插入图片描述

再重启微服务2001项目就可以了

Seata之Storage-Module说明

新建库存 Storage-Module:seata- storage - service2002

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>cloud2020</artifactId>
  7. <groupId>com.atguigu.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>seata- storage - service2002</artifactId>
  12. <dependencies>
  13. <!--nacos-->
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  17. </dependency>
  18. <!--seata-->
  19. <dependency>
  20. <groupId>com.alibaba.cloud</groupId>
  21. <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  22. <exclusions>
  23. <exclusion>
  24. <artifactId>seata-all</artifactId>
  25. <groupId>io.seata</groupId>
  26. </exclusion>
  27. </exclusions>
  28. </dependency>
  29. <dependency>
  30. <groupId>io.seata</groupId>
  31. <artifactId>seata-all</artifactId>
  32. <version>1.4.2</version>
  33. </dependency>
  34. <!--feign-->
  35. <dependency>
  36. <groupId>org.springframework.cloud</groupId>
  37. <artifactId>spring-cloud-starter-openfeign</artifactId>
  38. </dependency>
  39. <!--web-actuator-->
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-starter-web</artifactId>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-actuator</artifactId>
  47. </dependency>
  48. <!--mysql-druid-->
  49. <dependency>
  50. <groupId>mysql</groupId>
  51. <artifactId>mysql-connector-java</artifactId>
  52. <version>5.1.6</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>com.alibaba</groupId>
  56. <artifactId>druid-spring-boot-starter</artifactId>
  57. <version>1.1.10</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.mybatis.spring.boot</groupId>
  61. <artifactId>mybatis-spring-boot-starter</artifactId>
  62. <version>2.0.0</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.springframework.boot</groupId>
  66. <artifactId>spring-boot-starter-test</artifactId>
  67. <scope>test</scope>
  68. </dependency>
  69. <dependency>
  70. <groupId>org.projectlombok</groupId>
  71. <artifactId>lombok</artifactId>
  72. <optional>true</optional>
  73. </dependency>
  74. </dependencies>
  75. </project>

application.yml

  1. server:
  2. port: 2002
  3. spring:
  4. application:
  5. name: seata-storage-service
  6. cloud:
  7. alibaba:
  8. seata:
  9. tx-service-group: fsp_tx_group
  10. nacos:
  11. discovery:
  12. server-addr: localhost:8848
  13. datasource:
  14. driver-class-name: com.mysql.jdbc.Driver
  15. url: jdbc:mysql://localhost:3306/seata_storage
  16. username: root
  17. password: 123456
  18. logging:
  19. level:
  20. io:
  21. seata: info
  22. mybatis:
  23. mapperLocations: classpath:mapper/*.xml

在 resources 目录下新建 file.conf 文件, 将 seata 目录下的 conf/file.conf 复制进来 ,修改对应的数据库地址,数据库名,用户名,密码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. transport {
  2. # tcp, unix-domain-socket
  3. type = "TCP"
  4. #NIO, NATIVE
  5. server = "NIO"
  6. #enable heartbeat
  7. heartbeat = true
  8. # the client batch send request enable
  9. enableClientBatchSendRequest = false
  10. #thread factory for netty
  11. threadFactory {
  12. bossThreadPrefix = "NettyBoss"
  13. workerThreadPrefix = "NettyServerNIOWorker"
  14. serverExecutorThreadPrefix = "NettyServerBizHandler"
  15. shareBossWorker = false
  16. clientSelectorThreadPrefix = "NettyClientSelector"
  17. clientSelectorThreadSize = 1
  18. clientWorkerThreadPrefix = "NettyClientWorkerThread"
  19. # netty boss thread size
  20. bossThreadSize = 1
  21. #auto default pin or 8
  22. workerThreadSize = "default"
  23. }
  24. shutdown {
  25. # when destroy server, wait seconds
  26. wait = 3
  27. }
  28. serialization = "seata"
  29. compressor = "none"
  30. }
  31. ## transaction log store, only used in server side
  32. store {
  33. ## store mode: file、db
  34. mode = "db"
  35. ## file store property
  36. file {
  37. ## store location dir
  38. dir = "sessionStore"
  39. # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
  40. maxBranchSessionSize = 16384
  41. # globe session size , if exceeded throws exceptions
  42. maxGlobalSessionSize = 512
  43. # file buffer size , if exceeded allocate new buffer
  44. fileWriteBufferCacheSize = 16384
  45. # when recover batch read size
  46. sessionReloadReadSize = 100
  47. # async, sync
  48. flushDiskMode = async
  49. }
  50. ## database store property
  51. db {
  52. ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
  53. datasource = "druid"
  54. ## mysql/oracle/postgresql/h2/oceanbase etc.
  55. dbType = "mysql"
  56. driverClassName = "com.mysql.jdbc.Driver"
  57. ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
  58. url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
  59. user = "root"
  60. password = "123456"
  61. minConn = 5
  62. maxConn = 30
  63. globalTable = "global_table"
  64. branchTable = "branch_table"
  65. lockTable = "lock_table"
  66. queryLimit = 100
  67. }
  68. }
  69. ## server configuration, only used in server side
  70. server {
  71. recovery {
  72. #schedule committing retry period in milliseconds
  73. committingRetryPeriod = 1000
  74. #schedule asyn committing retry period in milliseconds
  75. asynCommittingRetryPeriod = 1000
  76. #schedule rollbacking retry period in milliseconds
  77. rollbackingRetryPeriod = 1000
  78. #schedule timeout retry period in milliseconds
  79. timeoutRetryPeriod = 1000
  80. }
  81. undo {
  82. logSaveDays = 7
  83. #schedule delete expired undo_log in milliseconds
  84. logDeletePeriod = 86400000
  85. }
  86. #check auth
  87. enableCheckAuth = true
  88. #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  89. maxCommitRetryTimeout = "-1"
  90. maxRollbackRetryTimeout = "-1"
  91. rollbackRetryTimeoutUnlockEnable = false
  92. retryDeadThreshold = 130000
  93. #新增
  94. #vgroup->rgroup
  95. ##fsp_tx_group是自定义的
  96. vgroup_mapping.my.test.tx_group="fsp_tx_group"
  97. }
  98. ## metrics configuration, only used in server side
  99. metrics {
  100. enabled = false
  101. registryType = "compact"
  102. # multi exporters use comma divided
  103. exporterList = "prometheus"
  104. exporterPrometheusPort = 9898
  105. }

在 resources 目录下新建 registry.conf文件, 将 seata 目录下的 conf/registry.conf 复制进来

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  3. type = "nacos"
  4. nacos {
  5. application = "seata-server"
  6. serverAddr = "127.0.0.1:8848"
  7. group = "SEATA_GROUP"
  8. namespace = ""
  9. cluster = "default"
  10. username = ""
  11. password = ""
  12. }
  13. eureka {
  14. serviceUrl = "http://localhost:8761/eureka"
  15. application = "default"
  16. weight = "1"
  17. }
  18. redis {
  19. serverAddr = "localhost:6379"
  20. db = 0
  21. password = ""
  22. cluster = "default"
  23. timeout = 0
  24. }
  25. zk {
  26. cluster = "default"
  27. serverAddr = "127.0.0.1:2181"
  28. sessionTimeout = 6000
  29. connectTimeout = 2000
  30. username = ""
  31. password = ""
  32. }
  33. consul {
  34. cluster = "default"
  35. serverAddr = "127.0.0.1:8500"
  36. aclToken = ""
  37. }
  38. etcd3 {
  39. cluster = "default"
  40. serverAddr = "http://localhost:2379"
  41. }
  42. sofa {
  43. serverAddr = "127.0.0.1:9603"
  44. application = "default"
  45. region = "DEFAULT_ZONE"
  46. datacenter = "DefaultDataCenter"
  47. cluster = "default"
  48. group = "SEATA_GROUP"
  49. addressWaitTime = "3000"
  50. }
  51. file {
  52. name = "file.conf"
  53. }
  54. }
  55. config {
  56. # file、nacos 、apollo、zk、consul、etcd3
  57. type = "nacos"
  58. nacos {
  59. serverAddr = "127.0.0.1:8848"
  60. namespace = ""
  61. group = "SEATA_GROUP"
  62. username = ""
  63. password = ""
  64. dataId = "seataServer.properties"
  65. }
  66. consul {
  67. serverAddr = "127.0.0.1:8500"
  68. aclToken = ""
  69. }
  70. apollo {
  71. appId = "seata-server"
  72. ## apolloConfigService will cover apolloMeta
  73. apolloMeta = "http://192.168.1.204:8801"
  74. apolloConfigService = "http://192.168.1.204:8080"
  75. namespace = "application"
  76. apolloAccesskeySecret = ""
  77. cluster = "seata"
  78. }
  79. zk {
  80. serverAddr = "127.0.0.1:2181"
  81. sessionTimeout = 6000
  82. connectTimeout = 2000
  83. username = ""
  84. password = ""
  85. nodePath = "/seata/seata.properties"
  86. }
  87. etcd3 {
  88. serverAddr = "http://localhost:2379"
  89. }
  90. file {
  91. name = "file.conf"
  92. }
  93. }

创建类 entity.CommonResult

  1. package com.atguigu.springcloud.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class CommonResult<T>
  9. {
  10. private Integer code;
  11. private String message;
  12. private T data;
  13. public CommonResult(Integer code, String message)
  14. {
  15. this(code,message,null);
  16. }
  17. }

创建类 entity.Storage

  1. package com.atguigu.springcloud.entity;
  2. import lombok.Data;
  3. @Data
  4. public class Storage {
  5. private Long id;
  6. /**
  7. * 产品id
  8. */
  9. private Long productId;
  10. /**
  11. * 总库存
  12. */
  13. private Integer total;
  14. /**
  15. * 已用库存
  16. */
  17. private Integer used;
  18. /**
  19. * 剩余库存
  20. */
  21. private Integer residue;
  22. }

Mapper接口及实现

创建接口 mapper.StorageMapper

  1. package com.atguigu.springcloud.mapper;
  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. @Mapper
  5. public interface StorageMapper {
  6. // 扣减库存
  7. void decrease(@Param("productId") Long productId, @Param("count") Integer count);
  8. }

mapper.StorageMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.atguigu.springcloud.mapper.StorageMapper">
  4. <!-- 扣减库存 -->
  5. <!-- void decrease(@Param("productId") Long productId, @Param("count") Integer count); -->
  6. <update id="decrease" parameterType="com.atguigu.springcloud.entity.Storage">
  7. update seata_storage.t_storage
  8. set used = used + #{count}, residue = residue - #{count}
  9. where product_id = #{productId}
  10. </update>
  11. <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entity.Storage">
  12. <id column="id" property="id" jdbcType="BIGINT"/>
  13. <result column="product_id" property="productId" jdbcType="BIGINT"/>
  14. <result column="total" property="total" jdbcType="INTEGER"/>
  15. <result column="used" property="used" jdbcType="INTEGER"/>
  16. <result column="residue" property="residue" jdbcType="INTEGER"/>
  17. </resultMap>
  18. </mapper>

Service接口及实现

创建接口 service.StorageService

  1. package com.atguigu.springcloud.service;
  2. public interface StorageService {
  3. // 扣减库存
  4. void decrease(Long productId, Integer count);
  5. }

创建类 service.impl.StorageServiceImpl

  1. package com.atguigu.springcloud.service.impl;
  2. import com.atguigu.springcloud.mapper.StorageMapper;
  3. import com.atguigu.springcloud.service.StorageService;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.stereotype.Service;
  6. import javax.annotation.Resource;
  7. @Service
  8. @Slf4j
  9. public class StorageServiceImpl implements StorageService {
  10. @Resource
  11. private StorageMapper storageMapper;
  12. // 扣减库存
  13. @Override
  14. public void decrease(Long productId, Integer count) {
  15. log.info("------->storage-service中扣减库存开始");
  16. storageMapper.decrease(productId, count);
  17. log.info("------->storage-service中扣减库存结束");
  18. }
  19. }

Controller

创建类 controller.StorageController

  1. package com.atguigu.springcloud.controller;
  2. import com.atguigu.springcloud.entity.CommonResult;
  3. import com.atguigu.springcloud.service.StorageService;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import javax.annotation.Resource;
  7. @RestController
  8. public class StorageController {
  9. @Resource
  10. private StorageService storageServicep;
  11. /**
  12. * 扣减库存
  13. */
  14. @RequestMapping("/storage/decrease")
  15. public CommonResult decrease(Long productId, Integer count) {
  16. storageServicep.decrease(productId, count);
  17. return new CommonResult(200, "扣减库存成功!");
  18. }
  19. }

Config 配置

config.MyBatisConfig

  1. package com.atguigu.springcloud.config;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @MapperScan(value = "com.atguigu.springcloud.mapper")
  6. public class MyBatisConfig {
  7. }

config.DataSourceProxyConfig

  1. package com.atguigu.springcloud.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import io.seata.rm.datasource.DataSourceProxy;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.mybatis.spring.SqlSessionFactoryBean;
  6. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.boot.context.properties.ConfigurationProperties;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  12. import javax.sql.DataSource;
  13. /**
  14. * 使用Seata对数据源进行代理
  15. */
  16. @Configuration
  17. public class DataSourceProxyConfig {
  18. @Value("${mybatis.mapperLocations}")
  19. private String mapperLocations;
  20. @Bean
  21. @ConfigurationProperties(prefix = "spring.datasource")
  22. public DataSource druidDataSource(){
  23. return new DruidDataSource();
  24. }
  25. @Bean
  26. public DataSourceProxy dataSourceProxy(DataSource dataSource) {
  27. return new DataSourceProxy(dataSource);
  28. }
  29. @Bean
  30. public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
  31. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  32. sqlSessionFactoryBean.setDataSource(dataSourceProxy);
  33. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
  34. sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
  35. return sqlSessionFactoryBean.getObject();
  36. }
  37. }

主启动类 SeataStorageMainApp2002

  1. package com.atguigu.springcloud;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.openfeign.EnableFeignClients;
  7. @EnableDiscoveryClient
  8. @EnableFeignClients
  9. //取消数据源的自动创建,而是使用自己定义的
  10. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
  11. public class SeataStorageMainApp2002 {
  12. public static void main(String[] args) {
  13. SpringApplication.run(SeataStorageMainApp2002.class, args);
  14. }
  15. }

启动微服务项目2002

在这里插入图片描述

Seata之Account-Module说明

新建账户 Account-Module:seata- account- service2003

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>cloud2020</artifactId>
  7. <groupId>com.atguigu.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>seata- account- service2003</artifactId>
  12. <dependencies>
  13. <!--nacos-->
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  17. </dependency>
  18. <!--seata-->
  19. <dependency>
  20. <groupId>com.alibaba.cloud</groupId>
  21. <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  22. <exclusions>
  23. <exclusion>
  24. <artifactId>seata-all</artifactId>
  25. <groupId>io.seata</groupId>
  26. </exclusion>
  27. </exclusions>
  28. </dependency>
  29. <dependency>
  30. <groupId>io.seata</groupId>
  31. <artifactId>seata-all</artifactId>
  32. <version>1.4.2</version>
  33. </dependency>
  34. <!--feign-->
  35. <dependency>
  36. <groupId>org.springframework.cloud</groupId>
  37. <artifactId>spring-cloud-starter-openfeign</artifactId>
  38. </dependency>
  39. <!--web-actuator-->
  40. <dependency>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-starter-web</artifactId>
  43. </dependency>
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-actuator</artifactId>
  47. </dependency>
  48. <!--mysql-druid-->
  49. <dependency>
  50. <groupId>mysql</groupId>
  51. <artifactId>mysql-connector-java</artifactId>
  52. <version>5.1.6</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>com.alibaba</groupId>
  56. <artifactId>druid-spring-boot-starter</artifactId>
  57. <version>1.1.10</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.mybatis.spring.boot</groupId>
  61. <artifactId>mybatis-spring-boot-starter</artifactId>
  62. <version>2.0.0</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.springframework.boot</groupId>
  66. <artifactId>spring-boot-starter-test</artifactId>
  67. <scope>test</scope>
  68. </dependency>
  69. <dependency>
  70. <groupId>org.projectlombok</groupId>
  71. <artifactId>lombok</artifactId>
  72. <optional>true</optional>
  73. </dependency>
  74. </dependencies>
  75. </project>

application.yml

  1. server:
  2. port: 2003
  3. spring:
  4. application:
  5. name: seata-account-service
  6. cloud:
  7. alibaba:
  8. seata:
  9. #自定义事务组名称需要与seata-server中的对应
  10. tx-service-group: fsp_tx_group
  11. nacos:
  12. discovery:
  13. server-addr: localhost:8848
  14. datasource:
  15. driver-class-name: com.mysql.jdbc.Driver
  16. url: jdbc:mysql://localhost:3306/seata_order
  17. username: root
  18. password: 123456
  19. feign:
  20. hystrix:
  21. enabled: false
  22. logging:
  23. level:
  24. io:
  25. seata: info
  26. mybatis:
  27. mapperLocations: classpath:mapper/*.xml

在 resources 目录下新建 file.conf 文件, 将 seata 目录下的 conf/file.conf 复制进来 ,修改对应的数据库地址,数据库名,用户名,密码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. transport {
  2. # tcp, unix-domain-socket
  3. type = "TCP"
  4. #NIO, NATIVE
  5. server = "NIO"
  6. #enable heartbeat
  7. heartbeat = true
  8. # the client batch send request enable
  9. enableClientBatchSendRequest = false
  10. #thread factory for netty
  11. threadFactory {
  12. bossThreadPrefix = "NettyBoss"
  13. workerThreadPrefix = "NettyServerNIOWorker"
  14. serverExecutorThreadPrefix = "NettyServerBizHandler"
  15. shareBossWorker = false
  16. clientSelectorThreadPrefix = "NettyClientSelector"
  17. clientSelectorThreadSize = 1
  18. clientWorkerThreadPrefix = "NettyClientWorkerThread"
  19. # netty boss thread size
  20. bossThreadSize = 1
  21. #auto default pin or 8
  22. workerThreadSize = "default"
  23. }
  24. shutdown {
  25. # when destroy server, wait seconds
  26. wait = 3
  27. }
  28. serialization = "seata"
  29. compressor = "none"
  30. }
  31. ## transaction log store, only used in server side
  32. store {
  33. ## store mode: file、db
  34. mode = "db"
  35. ## file store property
  36. file {
  37. ## store location dir
  38. dir = "sessionStore"
  39. # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
  40. maxBranchSessionSize = 16384
  41. # globe session size , if exceeded throws exceptions
  42. maxGlobalSessionSize = 512
  43. # file buffer size , if exceeded allocate new buffer
  44. fileWriteBufferCacheSize = 16384
  45. # when recover batch read size
  46. sessionReloadReadSize = 100
  47. # async, sync
  48. flushDiskMode = async
  49. }
  50. ## database store property
  51. db {
  52. ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
  53. datasource = "druid"
  54. ## mysql/oracle/postgresql/h2/oceanbase etc.
  55. dbType = "mysql"
  56. driverClassName = "com.mysql.jdbc.Driver"
  57. ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
  58. url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
  59. user = "root"
  60. password = "123456"
  61. minConn = 5
  62. maxConn = 30
  63. globalTable = "global_table"
  64. branchTable = "branch_table"
  65. lockTable = "lock_table"
  66. queryLimit = 100
  67. }
  68. }
  69. ## server configuration, only used in server side
  70. server {
  71. recovery {
  72. #schedule committing retry period in milliseconds
  73. committingRetryPeriod = 1000
  74. #schedule asyn committing retry period in milliseconds
  75. asynCommittingRetryPeriod = 1000
  76. #schedule rollbacking retry period in milliseconds
  77. rollbackingRetryPeriod = 1000
  78. #schedule timeout retry period in milliseconds
  79. timeoutRetryPeriod = 1000
  80. }
  81. undo {
  82. logSaveDays = 7
  83. #schedule delete expired undo_log in milliseconds
  84. logDeletePeriod = 86400000
  85. }
  86. #check auth
  87. enableCheckAuth = true
  88. #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  89. maxCommitRetryTimeout = "-1"
  90. maxRollbackRetryTimeout = "-1"
  91. rollbackRetryTimeoutUnlockEnable = false
  92. retryDeadThreshold = 130000
  93. #新增
  94. #vgroup->rgroup
  95. ##fsp_tx_group是自定义的
  96. vgroup_mapping.my.test.tx_group="fsp_tx_group"
  97. }
  98. ## metrics configuration, only used in server side
  99. metrics {
  100. enabled = false
  101. registryType = "compact"
  102. # multi exporters use comma divided
  103. exporterList = "prometheus"
  104. exporterPrometheusPort = 9898
  105. }

在 resources 目录下新建 registry.conf文件, 将 seata 目录下的 conf/registry.conf 复制进来

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  3. type = "nacos"
  4. nacos {
  5. application = "seata-server"
  6. serverAddr = "127.0.0.1:8848"
  7. group = "SEATA_GROUP"
  8. namespace = ""
  9. cluster = "default"
  10. username = ""
  11. password = ""
  12. }
  13. eureka {
  14. serviceUrl = "http://localhost:8761/eureka"
  15. application = "default"
  16. weight = "1"
  17. }
  18. redis {
  19. serverAddr = "localhost:6379"
  20. db = 0
  21. password = ""
  22. cluster = "default"
  23. timeout = 0
  24. }
  25. zk {
  26. cluster = "default"
  27. serverAddr = "127.0.0.1:2181"
  28. sessionTimeout = 6000
  29. connectTimeout = 2000
  30. username = ""
  31. password = ""
  32. }
  33. consul {
  34. cluster = "default"
  35. serverAddr = "127.0.0.1:8500"
  36. aclToken = ""
  37. }
  38. etcd3 {
  39. cluster = "default"
  40. serverAddr = "http://localhost:2379"
  41. }
  42. sofa {
  43. serverAddr = "127.0.0.1:9603"
  44. application = "default"
  45. region = "DEFAULT_ZONE"
  46. datacenter = "DefaultDataCenter"
  47. cluster = "default"
  48. group = "SEATA_GROUP"
  49. addressWaitTime = "3000"
  50. }
  51. file {
  52. name = "file.conf"
  53. }
  54. }
  55. config {
  56. # file、nacos 、apollo、zk、consul、etcd3
  57. type = "nacos"
  58. nacos {
  59. serverAddr = "127.0.0.1:8848"
  60. namespace = ""
  61. group = "SEATA_GROUP"
  62. username = ""
  63. password = ""
  64. dataId = "seataServer.properties"
  65. }
  66. consul {
  67. serverAddr = "127.0.0.1:8500"
  68. aclToken = ""
  69. }
  70. apollo {
  71. appId = "seata-server"
  72. ## apolloConfigService will cover apolloMeta
  73. apolloMeta = "http://192.168.1.204:8801"
  74. apolloConfigService = "http://192.168.1.204:8080"
  75. namespace = "application"
  76. apolloAccesskeySecret = ""
  77. cluster = "seata"
  78. }
  79. zk {
  80. serverAddr = "127.0.0.1:2181"
  81. sessionTimeout = 6000
  82. connectTimeout = 2000
  83. username = ""
  84. password = ""
  85. nodePath = "/seata/seata.properties"
  86. }
  87. etcd3 {
  88. serverAddr = "http://localhost:2379"
  89. }
  90. file {
  91. name = "file.conf"
  92. }
  93. }

创建类 entity.CommonResult

  1. package com.atguigu.springcloud.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class CommonResult<T>
  9. {
  10. private Integer code;
  11. private String message;
  12. private T data;
  13. public CommonResult(Integer code, String message)
  14. {
  15. this(code,message,null);
  16. }
  17. }

创建类 entity.Account

  1. package com.atguigu.springcloud.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.math.BigDecimal;
  6. @Data
  7. @AllArgsConstructor
  8. @NoArgsConstructor
  9. public class Account {
  10. private Long id;
  11. /**
  12. * 用户id
  13. */
  14. private Long userId;
  15. /**
  16. * 总额度
  17. */
  18. private BigDecimal total;
  19. /**
  20. * 已用额度
  21. */
  22. private BigDecimal used;
  23. /**
  24. * 剩余额度
  25. */
  26. private BigDecimal residue;
  27. }

Mapper接口及实现

创建接口 mapper.AccountMapper

  1. package com.atguigu.springcloud.mapper;
  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. import java.math.BigDecimal;
  5. @Mapper
  6. public interface AccountMapper {
  7. /**
  8. * 扣减账户余额
  9. */
  10. void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
  11. }

mapper.AccountMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.atguigu.springcloud.mapper.AccountMapper">
  4. <!-- 扣减账户余额 -->
  5. <!-- void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); -->
  6. <update id="decrease" parameterType="com.atguigu.springcloud.entity.Account">
  7. update seata_account.t_account
  8. set residue = residue - #{money}, used = used + #{money}
  9. where user_id = #{userId}
  10. </update>
  11. <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entity.Account">
  12. <id column="id" property="id" jdbcType="BIGINT"/>
  13. <result column="user_id" property="userId" jdbcType="BIGINT"/>
  14. <result column="total" property="total" jdbcType="DECIMAL"/>
  15. <result column="used" property="used" jdbcType="DECIMAL"/>
  16. <result column="residue" property="residue" jdbcType="DECIMAL"/>
  17. </resultMap>
  18. </mapper>

Service接口及实现

service.AccountService

  1. package com.atguigu.springcloud.service;
  2. import org.springframework.web.bind.annotation.RequestParam;
  3. import java.math.BigDecimal;
  4. public interface AccountService {
  5. /**
  6. * 扣减账户余额
  7. * @param userId 用户 id
  8. * @param money 金额
  9. */
  10. void decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
  11. }

service.impl.AccountServiceImpl

  1. package com.atguigu.springcloud.service.impl;
  2. import com.atguigu.springcloud.mapper.AccountMapper;
  3. import com.atguigu.springcloud.service.AccountService;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.stereotype.Service;
  6. import javax.annotation.Resource;
  7. import java.math.BigDecimal;
  8. @Service
  9. @Slf4j
  10. public class AccountServiceImpl implements AccountService {
  11. @Resource
  12. private AccountMapper accountMapper;
  13. /**
  14. * 扣减账户余额
  15. * @param userId 用户 id
  16. * @param money 金额
  17. */
  18. @Override
  19. public void decrease(Long userId, BigDecimal money) {
  20. log.info("------->account-service中扣减账户余额开始");
  21. accountMapper.decrease(userId,money);
  22. log.info("------->account-service中扣减账户余额结束");
  23. }
  24. }

Controller

controller.AccountController

  1. package com.atguigu.springcloud.controller;
  2. import com.atguigu.springcloud.entity.CommonResult;
  3. import com.atguigu.springcloud.service.AccountService;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.annotation.Resource;
  8. import java.math.BigDecimal;
  9. @RestController
  10. public class AccountController {
  11. @Resource
  12. private AccountService accountService;
  13. /**
  14. * 扣减账户余额
  15. * @param userId
  16. * @param money
  17. * @return
  18. */
  19. @RequestMapping("/account/decrease")
  20. public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money){
  21. accountService.decrease(userId, money);
  22. return new CommonResult(200,"扣减账户余额成功!");
  23. }
  24. }

Config配置

config.MyBatisConfig

  1. package com.atguigu.springcloud.config;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. @MapperScan("com.atguigu.springcloud.mapper")
  6. public class MyBatisConfig {
  7. }

config.DataSourceProxyConfig

  1. package com.atguigu.springcloud.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import io.seata.rm.datasource.DataSourceProxy;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.mybatis.spring.SqlSessionFactoryBean;
  6. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.boot.context.properties.ConfigurationProperties;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  12. import javax.sql.DataSource;
  13. /**
  14. * 使用Seata对数据源进行代理
  15. */
  16. @Configuration
  17. public class DataSourceProxyConfig {
  18. @Value("${mybatis.mapperLocations}")
  19. private String mapperLocations;
  20. @Bean
  21. @ConfigurationProperties(prefix = "spring.datasource")
  22. public DataSource druidDataSource(){
  23. return new DruidDataSource();
  24. }
  25. @Bean
  26. public DataSourceProxy dataSourceProxy(DataSource dataSource) {
  27. return new DataSourceProxy(dataSource);
  28. }
  29. @Bean
  30. public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
  31. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  32. sqlSessionFactoryBean.setDataSource(dataSourceProxy);
  33. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
  34. sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
  35. return sqlSessionFactoryBean.getObject();
  36. }
  37. }

主启动类 SeataAccountMainApp2003

  1. package com.atguigu.springcloud;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.openfeign.EnableFeignClients;
  7. @EnableDiscoveryClient
  8. @EnableFeignClients
  9. //取消数据源的自动创建,而是使用自己定义的
  10. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
  11. public class SeataAccountMainApp2003 {
  12. public static void main(String[] args) {
  13. SpringApplication.run(SeataAccountMainApp2003.class, args);
  14. }
  15. }

启动微服务2003

在这里插入图片描述

Seata之@GlobalTransactional验证

在这里插入图片描述

数据库初始情况:

在这里插入图片描述

正常下单 - http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

在这里插入图片描述

数据库正常下单后状况:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

超时异常,没加@GlobalTransactional

模拟AccountServiceImpl添加超时

  1. package com.atguigu.springcloud.service.impl;
  2. import com.atguigu.springcloud.mapper.AccountMapper;
  3. import com.atguigu.springcloud.service.AccountService;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.stereotype.Service;
  6. import javax.annotation.Resource;
  7. import java.math.BigDecimal;
  8. import java.util.concurrent.TimeUnit;
  9. @Service
  10. @Slf4j
  11. public class AccountServiceImpl implements AccountService {
  12. @Resource
  13. private AccountMapper accountMapper;
  14. /**
  15. * 扣减账户余额
  16. * @param userId 用户 id
  17. * @param money 金额
  18. */
  19. @Override
  20. public void decrease(Long userId, BigDecimal money) {
  21. log.info("------->account-service中扣减账户余额开始");
  22. //模拟超时异常,全局事务回滚
  23. //暂停几秒钟线程
  24. try {
  25. TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) {
  26. e.printStackTrace(); }
  27. accountMapper.decrease(userId,money);
  28. log.info("------->account-service中扣减账户余额结束");
  29. }
  30. }

超时下单 - http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

另外,OpenFeign的调用默认时间是1s以内,所以最后会抛异常。

在这里插入图片描述

数据库情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

故障情况

  • 当库存和账户金额扣减后,订单状态并没有设置为已经完成,没有从零改为1
  • 而且由于feign的重试机制,账户余额还有可能被多次扣减

超时异常,加了@GlobalTransactional

用@GlobalTransactional标注OrderServiceImpl的create()方法。

  1. package com.atguigu.springcloud.service.impl;
  2. import com.atguigu.springcloud.entity.Order;
  3. import com.atguigu.springcloud.mapper.OrderMapper;
  4. import com.atguigu.springcloud.service.AccountService;
  5. import com.atguigu.springcloud.service.OrderService;
  6. import com.atguigu.springcloud.service.StorageService;
  7. import io.seata.spring.annotation.GlobalTransactional;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.stereotype.Service;
  10. import javax.annotation.Resource;
  11. @Service
  12. @Slf4j
  13. public class OrderServiceImpl implements OrderService {
  14. @Resource
  15. private OrderMapper orderMapper;
  16. @Resource
  17. private StorageService storageService;
  18. @Resource
  19. private AccountService accountService;
  20. /**
  21. * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
  22. * 简单说:下订单->扣库存->减余额->改状态
  23. */
  24. // name = "fsp-create-order" 名称可以随便取,只要保证唯一性就可以
  25. //rollbackFor = Exception.class表示对任意异常都进行回滚
  26. @GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
  27. @Override
  28. public void create(Order order) {
  29. log.info("----->开始新建订单");
  30. // 1. 新建订单
  31. orderMapper.create(order);
  32. // 2. 扣减库存
  33. log.info("----->订单微服务开始调用库存,做扣减Count");
  34. storageService.decrease(order.getProductId(), order.getCount());
  35. log.info("----->订单微服务开始调用库存,做扣减end");
  36. // 3. 扣除账户余额
  37. log.info("----->订单微服务开始调用账户,做扣减Money");
  38. accountService.decrease(order.getUserId(), order.getMoney());
  39. log.info("----->订单微服务开始调用账户,做扣减end");
  40. // 4. 修改订单状态 从0到1,1代表已经完成
  41. log.info("----->修改订单状态开始");
  42. orderMapper.update(order.getUserId(), 0);
  43. log.info("----->修改订单状态结束");
  44. log.info("----->下订单结束了,O(∩_∩)O哈哈~");
  45. }
  46. }

还是模拟AccountServiceImpl添加超时,下单后数据库数据并没有任何改变,记录都添加不进来,达到出异常,数据库回滚的效果。

超时下单 - http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

Seata之原理简介

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

分布式事务的执行流程

在这里插入图片描述
在这里插入图片描述

AT模式如何做到对业务的无侵入

在这里插入图片描述

一阶段加载

在这里插入图片描述
在这里插入图片描述

二阶段提交

在这里插入图片描述

二阶段回滚

在这里插入图片描述
在这里插入图片描述

补充

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,230人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Seata 案例

    分布式事务问题由来 分布式前 ![在这里插入图片描述][8e66a18cc37f405d87b0548023924cb2.png] 分布式后 ![在这里插入图片描述

    相关 Seata】初识Seata

    Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。 官网地址:

    相关 Seata

    目录 1、分布式事务产生的背景? 2、常见的分布式事务解决方案? 3、什么是Seata? 4、Seata分布式事务框架实现原理? 5、SpringBoot如何整合Se