Spring Cloud Alibaba Seata 实现 TCC 事物

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案

Seata 官网:https://seata.io/zh-cn/

Spring Cloud Alibaba 官网:https://sca.aliyun.com/zh-cn/
 

版本说明

SpringBoot 版本 2.6.5

SpringCloud 版本 2021.0.1

SpringCloudAlibaba 版本 2021.0.1.0

本文详细说明

数据库服务器版本 mysql 8.0.25

mybatis plus 版本 3.5.1

nacos 版本 1.4.2

seata 客户端版本 1.4.2

seata 服务端版本 1.7.1

本文讲解的是 seata 的 TCC 事物模型,在开始阅读下面内容之前,建议先阅读笔者的这篇文章《Spring Cloud Alibaba Seata 实现分布式事物》,这篇文章中实现的是 seata 的 AT 事物,且笔者的本篇文章《Spring Cloud Alibaba Seata 实现 TCC 事物》是在《Spring Cloud Alibaba Seata 实现分布式事物》基础上写的,很多内容需要先了解,涉及seata 和nacos的重复内容,笔者在本篇文章中不在赘述,因此建议读者先看《Spring Cloud Alibaba Seata 实现分布式事物》,之后再学习本篇文章。当然,如果你对 seata 的搭建已经非常熟悉,那么可以直接开始下面阅读

目录

1、创建项目

        1.1、新建 maven 聚合项目 cloud-learn

        1.2、创建 account 服务

        1.3、创建 order 服务

2、添加配置

        2.1、客户端配置

        2.2、服务端配置

3、数据库建表

        3.1、seata 服务端建表

        3.2、seata 客户端建表

4、运行测试

5、项目代码


1、创建项目

1.1、新建 maven 聚合项目 cloud-learn

最外层父工程 cloud-learn 的 pom.xml



    4.0.0

    com.wsjzzcbq
    cloud-learn
    1.0-SNAPSHOT
    
        gateway-learn
        consumer-learn
        sentinel-learn
        seata-at-account-learn
        seata-at-order-learn
        seata-tcc-order-learn
        seata-tcc-account-learn
    
    pom

    
        
            naxus-aliyun
            naxus-aliyun
            https://maven.aliyun.com/repository/public
            
                true
            
            
                false
            
        
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.5
        
    

    
        2021.0.1
        2021.0.1.0
        2021.1
        2021.1
        3.1.1
        1.1.17
        8.0.11
        3.5.1
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            

            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            

            
                com.alibaba.cloud
                spring-cloud-starter-alibaba-nacos-discovery
                ${alibaba-nacos-discovery.veriosn}
            

            
                com.alibaba.cloud
                spring-cloud-starter-alibaba-nacos-config
                ${alibaba-nacos-config.version}
            

            
            
                org.springframework.cloud
                spring-cloud-starter-bootstrap
                ${spring-cloud-starter-bootstrap.version}
            

            
                com.alibaba.fastjson2
                fastjson2
                2.0.40
            
        
    

    
        
            org.projectlombok
            lombok
        
    


下面会创建2个服务 account 和 order,模拟用户下订单后扣减账户金额,服务间使用 feign 调用,因为 account 和 order 服务使用不同的数据库,因此产生分布式事物,使用 seata 解决

1.2、创建 account 服务

创建子工程 seata-tcc-account-learn

seata-tcc-account-learn pom 文件



    
        cloud-learn
        com.wsjzzcbq
        1.0-SNAPSHOT
    
    4.0.0

    seata-tcc-account-learn

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        

        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-seata
        

        
            com.alibaba
            druid-spring-boot-starter
            ${druid.version}
        

        
            mysql
            mysql-connector-java
            ${mysql.version}
        

        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

启动类 SeataTCCAccountApplication

package com.wsjzzcbq;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SeataTCCAccountApplication
 *
 * @author wsjz
 * @date 2023/10/20
 */
@MapperScan(value = {"com.wsjzzcbq.mapper"})
@SpringBootApplication
public class SeataTCCAccountApplication {

    public static void main(String[] args) {
        SpringApplication.run(SeataTCCAccountApplication.class, args);
    }
}

实体类 Account

package com.wsjzzcbq.bean;

import lombok.Data;

/**
 * Account
 *
 * @author wsjz
 * @date 2022/07/07
 */
@Data
public class Account {

    private Integer id;

    private String userId;

    private Integer money;
}

AccountMapper

package com.wsjzzcbq.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wsjzzcbq.bean.Account;

/**
 * AccountMapper
 *
 * @author wsjz
 * @date 2023/10/13
 */
public interface AccountMapper extends BaseMapper {
}

AccountReduceAction TCC 方法声明

package com.wsjzzcbq.action;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

/**
 * AccountReduceAction
 *
 * @author wsjz
 * @date 2023/10/16
 */
@LocalTCC
public interface AccountReduceAction {

    @TwoPhaseBusinessAction(name = "account-reduce", commitMethod = "commit", rollbackMethod = "cancel")
    boolean reduce(@BusinessActionContextParameter(paramName = "userId") String userId, @BusinessActionContextParameter(paramName = "money") int money);

    boolean commit(BusinessActionContext businessActionContext);

    boolean cancel(BusinessActionContext businessActionContext);
}

这里是TCC事物,需要定义接口,加上@LocalTCC 注解标识,@TwoPhaseBusinessAction 注解声明commit 和 rollback 对应的方法,TCC 中 commit 和rollback 可阶段需要的参数可通过 @BusinessActionContextParameter 注解传递,接收时通过 BusinessActionContext 获取

AccountReduceActionImpl TCC的实际逻辑

package com.wsjzzcbq.action.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.wsjzzcbq.action.AccountReduceAction;
import com.wsjzzcbq.bean.Account;
import com.wsjzzcbq.service.AccountService;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * AccountReduceActionImpl
 *
 * @author wsjz
 * @date 2023/10/16
 */
@Slf4j
@Component
public class AccountReduceActionImpl implements AccountReduceAction {

    @Autowired
    private AccountService accountService;

    @Override
    public boolean reduce(String userId, int money) {
        UpdateWrapper up = new UpdateWrapper<>();
        String sql = "money = money - " + money;
        up.setSql(sql);
        up.eq("user_id", userId);
        accountService.update(up);
        return true;
    }

    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        String userId = String.valueOf(businessActionContext.getActionContext("userId"));
        System.out.println(userId);
        log.info("提交成功");
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext businessActionContext) {
        String userId = String.valueOf(businessActionContext.getActionContext("userId"));
        int money = (int) businessActionContext.getActionContext("money");
        LambdaQueryWrapper lqw = new LambdaQueryWrapper<>();
        lqw.eq(Account::getUserId, userId);
        Account account = accountService.getOne(lqw);

        UpdateWrapper uw = new UpdateWrapper<>();
        uw.set("money", account.getMoney() + money);
        uw.eq("user_id", userId);
        accountService.update(uw);
        log.info("回滚成功");
        return true;
    }
}

AccountService

package com.wsjzzcbq.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.wsjzzcbq.bean.Account;

/**
 * AccountService
 *
 * @author wsjz
 * @date 2023/10/13
 */
public interface AccountService extends IService {

    String reduce(String userId, int money);

}

AccountServiceImpl

package com.wsjzzcbq.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsjzzcbq.action.AccountReduceAction;
import com.wsjzzcbq.bean.Account;
import com.wsjzzcbq.mapper.AccountMapper;
import com.wsjzzcbq.service.AccountService;
import io.seata.core.context.RootContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * AccountServiceImpl
 *
 * @author wsjz
 * @date 2023/10/13
 */
@Service
public class AccountServiceImpl extends ServiceImpl implements AccountService {

    @Autowired
    private AccountReduceAction accountReduceAction;

    @Override
    public String reduce(String userId, int money) {
        String xid = RootContext.getXID();
        System.out.println(xid);

        accountReduceAction.reduce(userId, money);
        return "ok";
    }
}

AccountController

package com.wsjzzcbq.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wsjzzcbq.bean.Account;
import com.wsjzzcbq.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * AccountController
 *
 * @author wsjz
 * @date 2023/10/13
 */
@RequestMapping("/account")
@RestController
public class AccountController {

    @Autowired
    private AccountService accountService;

    @GetMapping("/find")
    public String find() throws JsonProcessingException {
        Account account = accountService.list().get(0);
        ObjectMapper objectMapper = new ObjectMapper();
        String res = objectMapper.writeValueAsString(account);
        System.out.println(res);
        return res;
    }

    @RequestMapping("/reduce")
    public String debit(String userId, int money) {
        try {
            accountService.reduce(userId, money);
            return "扣款成功";
        } catch (Exception e) {
            return "扣款失败";
        }
    }
}

application.yml 文件

server:
  port: 9001
spring:
  main:
    allow-circular-references: true
  application:
    name: seata-tcc-account-learn
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.3.232:3306/pmc-account?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:
      username: nacos
      password: nacos
      server-addr: 192.168.2.140
      discovery:
        namespace: public
#        server-addr: 192.168.2.140
#      config:
#        server-addr:





seata:
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.server-addr}
      username: ${spring.cloud.nacos.username}
      password: ${spring.cloud.nacos.password}
      group: SEATA_GROUP
      data-id: seata-tcc.properties
  registry:
    type: nacos
    nacos:
      application: seata-server
      cluster: default
      server-addr: ${spring.cloud.nacos.server-addr}
      username: ${spring.cloud.nacos.username}
      password: ${spring.cloud.nacos.password}
      group: SEATA_GROUP
# 事物分组,如果不配置默认是spring.application.name + '-seata-service-group'
#  tx-service-group:


logging:
  level:
    com.wsjzzcbq.mapper: debug

配置参数说明可以看《Spring Cloud Alibaba Seata 实现分布式事物》,这里不再赘述

1.3、创建 order 服务

创建子工程 seata-tcc-order-learn 项目

seata-tcc-order-learn pom 文件



    
        cloud-learn
        com.wsjzzcbq
        1.0-SNAPSHOT
    
    4.0.0

    seata-tcc-order-learn

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        

        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.cloud
            spring-cloud-starter-loadbalancer
        

        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-seata
        

        
            com.alibaba
            druid-spring-boot-starter
            ${druid.version}
        

        
            mysql
            mysql-connector-java
            ${mysql.version}
        

        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


启动类 SeataTCCOrderApplication

package com.wsjzzcbq;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * SeataTCCOrderApplication
 *
 * @author wsjz
 * @date 2023/10/20
 */
@MapperScan(value = {"com.wsjzzcbq.mapper"})
@EnableFeignClients
@SpringBootApplication
public class SeataTCCOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(SeataTCCOrderApplication.class, args);
    }
}

订单实体类 Order

package com.wsjzzcbq.bean;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * Order
 *
 * @author wsjz
 * @date 2022/07/07
 */
@TableName("order_tbl")
@Data
public class Order {

    @TableId
    private Integer id;

    private String userId;

    private String code;

    private Integer count;

    private Integer money;
}

OrderMapper

package com.wsjzzcbq.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wsjzzcbq.bean.Order;

/**
 * OrderMapper
 *
 * @author wsjz
 * @date 2022/07/07
 */
public interface OrderMapper extends BaseMapper {
}

AccountFeign

package com.wsjzzcbq.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * AccountFeign
 *
 * @author wsjz
 * @date 2023/10/13
 */
@FeignClient(value = "seata-tcc-account-learn")
public interface AccountFeign {

    @RequestMapping("/account/reduce")
    String debit(@RequestParam("userId") String userId, @RequestParam("money") int money);
}

OrderServiceCreateAction TCC 方法定义

package com.wsjzzcbq.action;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

/**
 * OrderServiceCreateAction
 *
 * @author wsjz
 * @date 2023/10/16
 */
@LocalTCC
public interface OrderServiceCreateAction {

    @TwoPhaseBusinessAction(name = "order-create", commitMethod = "commit", rollbackMethod = "cancel")
    void create(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money") int money,
                @BusinessActionContextParameter(paramName = "orderCode") String orderCode,
                @BusinessActionContextParameter(paramName = "rollback") boolean rollback);

    boolean commit(BusinessActionContext businessActionContext);

    boolean cancel(BusinessActionContext businessActionContext);
}

OrderServiceCreateActionImpl TCC实际方法

package com.wsjzzcbq.action.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wsjzzcbq.action.OrderServiceCreateAction;
import com.wsjzzcbq.bean.Order;
import com.wsjzzcbq.service.OrderService;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * OrderServiceCreateActionImpl
 *
 * @author wsjz
 * @date 2023/10/16
 */
@Slf4j
@Component
public class OrderServiceCreateActionImpl implements OrderServiceCreateAction {

    @Autowired
    private OrderService orderService;

    @Override
    public void create(String userId, int money, String orderCode, boolean rollback) {

        Order order = new Order();
        order.setCode(orderCode);
        order.setCount(1);
        order.setUserId(userId);
        order.setMoney(money);

        orderService.save(order);
    }

    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        System.out.println("money");
        System.out.println(businessActionContext.getActionContext("money"));
        System.out.println(businessActionContext.getActionContext("orderCode"));
        log.info("提交成功");
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext businessActionContext) {
        String orderCode = String.valueOf(businessActionContext.getActionContext("orderCode"));
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code", orderCode);
        orderService.remove(queryWrapper);
        log.info("回滚成功");
        return true;
    }
}

OrderService

package com.wsjzzcbq.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.wsjzzcbq.bean.Order;

/**
 * OrderService
 *
 * @author wsjz
 * @date 2022/07/07
 */
public interface OrderService extends IService {

    void create(String userId, int money, boolean rollback);
}

OrderServiceImpl

package com.wsjzzcbq.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsjzzcbq.action.OrderServiceCreateAction;
import com.wsjzzcbq.bean.Order;
import com.wsjzzcbq.feign.AccountFeign;
import com.wsjzzcbq.mapper.OrderMapper;
import com.wsjzzcbq.service.OrderService;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;

/**
 * OrderServiceImpl
 *
 * @author wsjz
 * @date 2022/07/07
 */
@Service
public class OrderServiceImpl extends ServiceImpl implements OrderService {

    @Autowired
    private AccountFeign accountFeign;

    @Autowired
    private OrderServiceCreateAction orderServiceCreateAction;

    @GlobalTransactional
    @Override
    public void create(String userId, int money, boolean rollback) {
        String xid = RootContext.getXID();
        System.out.println(xid);

        String orderCode = UUID.randomUUID().toString();
        orderServiceCreateAction.create(userId, money, orderCode, rollback);

        accountFeign.debit(userId, money);

        if (rollback) {
            int a = 1/0;
        }

    }
}

OrderController

package com.wsjzzcbq.controller;

import com.wsjzzcbq.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * OrderController
 *
 * @author wsjz
 * @date 2022/07/09
 */
@RequestMapping("/order")
@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * http://localhost:9002/order/create?userId=101&money=10&rollback=false
     * @param userId
     * @param money
     * @param rollback
     * @return
     */
    @RequestMapping("/create")
    public String create(String userId, int money, boolean rollback) {
        try {
            orderService.create(userId, money, rollback);
            return "下单成功";
        } catch (Exception e) {
            e.printStackTrace();
            return "下单失败";
        }
    }

}

application.yml 文件

server:
  port: 9002
spring:
  main:
    allow-circular-references: true
  application:
    name: seata-tcc-order-learn
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.3.232:3306/pmc-order?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:
      username: nacos
      password: nacos
      server-addr: 192.168.2.140
      discovery:
        namespace: public
#        server-addr: 192.168.2.140
#      config:
#        server-addr:





seata:
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.server-addr}
      username: ${spring.cloud.nacos.username}
      password: ${spring.cloud.nacos.password}
      group: SEATA_GROUP
      data-id: seata-tcc.properties
  registry:
    type: nacos
    nacos:
      application: seata-server
      cluster: default
      server-addr: ${spring.cloud.nacos.server-addr}
      username: ${spring.cloud.nacos.username}
      password: ${spring.cloud.nacos.password}
      group: SEATA_GROUP
# 事物分组,如果不配置默认是spring.application.name + '-seata-service-group'
  tx-service-group: seata-tcc-account-learn-seata-service-group

logging:
  level:
    com.wsjzzcbq.mapper: debug

mybatis-plus:
  global-config:
    db-config:
      id-type: auto

2、添加配置

2.1、客户端配置

在nacos上新建 group 是 SEATA_GROUP,data-id 是 seata-tcc.properties 的配置,内容如下

seata-tcc.properties

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
service.vgroupMapping.seata-tcc-account-learn-seata-service-group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
# You can choose from the following options: fastjson, jackson, gson
tcc.contextJsonParserType=fastjson

#Log rule configuration, for client and server
log.exceptionRate=100

seata-tcc.properties 在《Spring Cloud Alibaba Seata 实现分布式事物》的 seata.properties 基础上修改事物分组即可

Spring Cloud Alibaba Seata 实现 TCC 事物_第1张图片

2.2、服务端配置

服务端配置和《Spring Cloud Alibaba Seata 实现分布式事物》保持一致,无需修改

3、数据库建表

3.1、seata 服务端建表

看《Spring Cloud Alibaba Seata 实现分布式事物》seata 服务端建表,保持一致,无需修改

3.2、seata 客户端建表

看《Spring Cloud Alibaba Seata 实现分布式事物》seata 客户端建表

undo_log 表不需要,保留 account 和 order_tbl 表即可

4、运行测试

启动 seata-server-1.7.1

进入 bin 目录,双击 seata-server.bat

Spring Cloud Alibaba Seata 实现 TCC 事物_第2张图片

启动 account 和 order 服务

nacos 服务和配置

Spring Cloud Alibaba Seata 实现 TCC 事物_第3张图片

测试正常情况

浏览器请求:http://localhost:9002/order/create?userId=101&money=10&rollback=false

扣减账户 10 元,新增订单

Spring Cloud Alibaba Seata 实现 TCC 事物_第4张图片

测试回滚情况

5、项目代码

码云地址:https://gitee.com/wsjzzcbq/csdn-blog/tree/master/cloud-learn

至此完

你可能感兴趣的:(spring,cloud,springcloud,微服务,java,分布式,1024程序员节)