Giter Site home page Giter Site logo

seata-nacos-cloud's Introduction

基于 spring spring-cloud-alibaba nacos seata 的分布式事务 项目

通过本文你能学会 基于 seata nacos 的分布式事务解决方案

  1. 搭建 nacos-server docker服务 假设目录在/docker/docker-compose/nacos
    • 1 创建nacos server docker-compose.yml 文件
    version: "3.9"
    services:
     nacos:
       image: nacos/nacos-server:v2.2.3
       container_name: nacos-standalone-mysql
       env_file:
         - ./env/nacos-standlone-mysql.env
       environment:
         - NACOS_AUTH_ENABLE=true
         - NACOS_AUTH_TOKEN_EXPIRE_SECONDS=1800
         - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
       volumes:
         - ./standalone-logs/:/home/nacos/logs
         - ./conf/application.properties:/home/nacos/conf/application.properties
       ports:
         - "8848:8848"
         - "9848:9848"
       depends_on:
         mysql:
           condition: service_healthy
       restart: always
     mysql:
       container_name: mysql
       build:
         context: .
         dockerfile: ./image/mysql/8/Dockerfile
       image: example/mysql:8.0.30
       env_file:
         - ./env/mysql.env
       volumes:
         - ./mysql:/var/lib/mysql
       ports:
         - "3306:3306"
       healthcheck:
         test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
         interval: 5s
         timeout: 10s
         retries: 10
    • 2 启动 nacos server 进入 /docker/docker-compose/nacos docker-compose up -d
    • 3 访问 nacos server (http://192.168.50.15:8848/nacos) 账号密码都是nacos
    • 4 在nacos上创建seata的命名空间,需要记住的是 命名空间的id ,这个值在后期需要用到。
    • 5 运行nacos-config.sh导入 seata 的配置到 nacos中对应的命名空间namespace中 主要是db部分
    • 6 官方导入脚本代码
    • 7 我用官方脚本导入报错。 107行的配置数据,我参考脚本用postman自己手动一条一条导入的。。。
    #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.default_tx_group=default
    #If you use a registry, you can ignore it
    service.default.grouplist=192.168.50.15: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
    
    #Log rule configuration, for client and server
    log.exceptionRate=100
    
    #Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
    store.mode=db
    store.lock.mode=file
    store.session.mode=file
    #Used for password encryption
    store.publicKey=
    
    #If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
    store.file.dir=file_store/data
    store.file.maxBranchSessionSize=16384
    store.file.maxGlobalSessionSize=512
    store.file.fileWriteBufferCacheSize=16384
    store.file.flushDiskMode=async
    store.file.sessionReloadReadSize=100
    
    #These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
    store.db.datasource=druid
    store.db.dbType=mysql
    store.db.driverClassName=com.mysql.cj.jdbc.Driver
    store.db.url=jdbc:mysql://192.168.50.15:3306/seata_dev?rewriteBatchedStatements=true&&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
    store.db.user=root
    store.db.password=root
    store.db.minConn=5
    store.db.maxConn=50
    store.db.globalTable=global_table
    store.db.branchTable=branch_table
    store.db.distributedLockTable=distributed_lock
    store.db.queryLimit=100
    store.db.lockTable=lock_table
    store.db.maxWait=5000
    
    #These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
    store.redis.mode=single
    store.redis.single.host=127.0.0.1
    store.redis.single.port=6379
    store.redis.sentinel.masterName=
    store.redis.sentinel.sentinelHosts=
    store.redis.maxConn=10
    store.redis.minConn=1
    store.redis.maxTotal=100
    store.redis.database=0
    store.redis.password=
    store.redis.queryLimit=100
    
    #Transaction rule configuration, only for the server
    server.recovery.committingRetryPeriod=1000
    server.recovery.asynCommittingRetryPeriod=1000
    server.recovery.rollbackingRetryPeriod=1000
    server.recovery.timeoutRetryPeriod=1000
    server.maxCommitRetryTimeout=-1
    server.maxRollbackRetryTimeout=-1
    server.rollbackRetryTimeoutUnlockEnable=false
    server.distributedLockExpireTime=10000
    server.xaerNotaRetryTimeout=60000
    server.session.branchAsyncQueueSize=5000
    server.session.enableBranchAsyncRemove=false
    server.enableParallelRequestHandle=false
    
    #Metrics configuration, only for the server
    metrics.enabled=false
    metrics.registryType=compact
    metrics.exporterList=prometheus
    metrics.exporterPrometheusPort=9898
    
  2. 搭建 seata-server docker服务 假设目录在/docker/docker-compose/seata
    • 1 为了复制要修改的配置目录 先用docker run 启动一个默认的seata-server
    • 2 启动一个临时seata server ,复制要修改的资源目录
    # 
    docker run -d -rm --name seata-temp seataio/seata-server:1.6.1
    docker cp seata-temp:/seata-server/resources /docker/docker-compose/seata/resources
    • 3 参考 /docker/docker-compose/seata/resources/application.example.yml 修改seata-server 配置文件 /docker/docker-compose/seata/resources/application.yml
    server:
     port: 7091
    
    spring:
     application:
       name: seata-server
    
    logging:
     config: classpath:logback-spring.xml
     file:
       path: ${user.home}/logs/seata
     extend:
       logstash-appender:
         destination: 127.0.0.1:4560
       kafka-appender:
         bootstrap-servers: 127.0.0.1:9092
         topic: logback_to_logstash
    
    console:
     user:
       username: seata
       password: seata
    
    seata:
     config:
       # support: nacos, consul, apollo, zk, etcd3
       type: nacos
       nacos:
         server-addr: 192.168.50.15:8848
         namespace: 82eca4a8-ef2f-488e-9ebd-cb70bb2f060a
         group: SEATA_GROUP
         username: nacos
         password: nacos
         context-path:
         ##if use MSE Nacos with auth, mutex with username/password attribute
         #access-key:
         #secret-key:
         data-id: seata.properties
     
     registry:
       # support: nacos, eureka, redis, zk, consul, etcd3, sofa
       type: nacos
       nacos:
         application: seata-server
         server-addr: 192.168.50.15:8848
         group: SEATA_GROUP
         namespace: 82eca4a8-ef2f-488e-9ebd-cb70bb2f060a
         cluster: default
         username: nacos
         password: nacos
         context-path:
         ##if use MSE Nacos with auth, mutex with username/password attribute
         #access-key:
         #secret-key:
     
     store:
       # support: file 、 db 、 redis
       mode: db
       db:
         datasource: druid
         db-type: mysql
         driver-class-name: com.mysql.cj.jdbc.Driver
         url: jdbc:mysql://192.168.50.15:3306/seata_dev?rewriteBatchedStatements=true&&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
         user: root
         password: root
         min-conn: 10
         max-conn: 100
         global-table: global_table
         branch-table: branch_table
         lock-table: lock_table
         distributed-lock-table: distributed_lock
         query-limit: 1000
         max-wait: 5000
    
     server:
       service-port: 8091 #If not configured, the default is '${server.port} + 1000'
     security:
       secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
       tokenValidityInMilliseconds: 1800000
       ignore:
       urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
    • 4 停止临时的seata-temp
    docker stop seata-temp
    • 5 创建 seata server docker-compose.yml
    version: "3.9"
    services:
      seata-server:
        image: seataio/seata-server:1.6.1
        hostname: seata-server
        ports:
          - "7091:7091"
          - "8091:8091"
        environment:
          # 时区设置为东8区
          - TZ=Asia/Shanghai
          # 可不用, 会从配置中心读取, 加上这个万一配置中心配置有问题加载不到配置时会报错可提前发现问题
          - STORE_MODE=db
          # 以SEATA_IP作为host注册到注册中心,使用宿主机ip
          - SEATA_IP=192.168.50.15
          - SEATA_PORT=8091
         # 官方说明的registry.conf 貌似是不生效的 至少我测试了很久1.61的seata docker是这样 我映射这个的话 启动会一直报127的mysql连接失败不知道为什么官方文档也不统一 而且不能只映射application.yml一个文件 得映射整个文件夹 - -
         #- SEATA_CONFIG_NAME=file:/root/seata-config/registry
        volumes:
          # 时区设置为东8区 上面的环境变量有时不生效
          - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
          - ./resources:/seata-server/resources
    • 6 启动 seata server 进入/docker/docker-compose/seata 运行 docker-compose up -d
  3. 访问 seata server (http://192.168.50.15:7091/#/TransactionInfo) 账号密码都是 seata
  4. 初始化项目sql

不同的业务表理论上来说是在不同的库中的, 可以一个服务建一个库。 你也可以先放一个库开发测试。
请注意每个使用seata-server的微服务的数据库都必须得有 undo_log 表!

-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`
(
    `id`      int(11) NOT NULL AUTO_INCREMENT,
    `user_id` varchar(255) DEFAULT NULL,
    `amount`  double(14, 2
                  ) DEFAULT '0.00',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account`
VALUES ('1', '1', '4000.00');

-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`
(
    `id`             int(11) NOT NULL AUTO_INCREMENT,
    `order_no`       varchar(255) DEFAULT NULL,
    `user_id`        varchar(255) DEFAULT NULL,
    `commodity_code` varchar(255) DEFAULT NULL,
    `count`          int(11) DEFAULT '0',
    `amount`         double(14, 2
                         ) DEFAULT '0.00',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_order
-- ----------------------------

-- ----------------------------
-- Table structure for t_stock
-- ----------------------------
DROP TABLE IF EXISTS `t_stock`;
CREATE TABLE `t_stock`
(
    `id`             int(11) NOT NULL AUTO_INCREMENT,
    `commodity_code` varchar(255) DEFAULT NULL,
    `name`           varchar(255) DEFAULT NULL,
    `count`          int(11) DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_stock
-- ----------------------------
INSERT INTO `t_stock`
VALUES ('1', 'C202306121800', '水杯', '1000');

-- ----------------------------
-- Table structure for undo_log
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
-- ----------------------------
DROP TABLE IF EXISTS `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,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

请求前 DB 数据如下

t_account 表数据

id user_id account
1 1 4000

t_account 表数据

id commodity_code name count
1 C202306121800 水杯 1000

t_order 表无数据生成

  1. 启动 微服务 的 business account stock order
  2. 访问 nacos 检查 当前的服务是否都正确注册到 nacos server
  3. GET 请求 http://127.0.0.1/business/toOrder?userId=1&commodityCode=C202306121800&count=2000&amount=4000

服务入口为 business 服务下的 BusinessController

@RestController
@RequestMapping("/business")
public class BusinessController {

    @Resource
    private OrderFeign orderFeign;
    @Resource
    private StockFeign stockFeign;
    @Resource
    private AccountFeign accountFeign;


    @GlobalTransactional
    @RequestMapping("/toOrder")
    public void toOrder (String userId, String commodityCode, Integer count, BigDecimal amount) {
        accountFeign.reduce(userId, amount);
        stockFeign.deduct(commodityCode, count);
        orderFeign.add(userId, commodityCode, count, amount);
    }
}

business 服务日志如下business 服务日志如下

2023-06-12 18:29:06.317  INFO 33872 --- [p-nio-80-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-06-12 18:29:06.317  INFO 33872 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-06-12 18:29:06.317  INFO 33872 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2023-06-12 18:29:06.347  INFO 33872 --- [p-nio-80-exec-1] io.seata.tm.TransactionManagerHolder     : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@606d2bad
2023-06-12 18:29:06.383  INFO 33872 --- [p-nio-80-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : Begin new global transaction [192.168.50.15:8091:18416337030565889]
2023-06-12 18:29:07.216  INFO 33872 --- [p-nio-80-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : Suspending current transaction, xid = 192.168.50.15:8091:18416337030565889
2023-06-12 18:29:07.217  INFO 33872 --- [p-nio-80-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.50.15:8091:18416337030565889] rollback status: Rollbacked
2023-06-12 18:29:07.229 ERROR 33872 --- [p-nio-80-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$InternalServerError: [500] during [GET] to [http://stock/stock/deduct?commodityCode=C202306121800&count=2000] [StockFeign#deduct(String,Integer)]: [{"timestamp":"2023-06-12T10:29:07.147+00:00","status":500,"error":"Internal Server Error","path":"/stock/deduct"}]] with root cause

feign.FeignException$InternalServerError: [500] during [GET] to [http://stock/stock/deduct?commodityCode=C202306121800&count=2000] [StockFeign#deduct(String,Integer)]: [{"timestamp":"2023-06-12T10:29:07.147+00:00","status":500,"error":"Internal Server Error","path":"/stock/deduct"}]
	at feign.FeignException.serverErrorStatus(FeignException.java:250) ~[feign-core-11.10.jar:na]
	at feign.FeignException.errorStatus(FeignException.java:197) ~[feign-core-11.10.jar:na]
	at feign.FeignException.errorStatus(FeignException.java:185) ~[feign-core-11.10.jar:na]
	...

account 服务日志如下

2023-06-12 18:29:06.494  INFO 8076 --- [p-nio-81-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-06-12 18:29:06.494  INFO 8076 --- [p-nio-81-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-06-12 18:29:06.495  INFO 8076 --- [p-nio-81-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@46fda57c] was not registered for synchronization because synchronization is not active
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.baomidou.mybatisplus.core.toolkit.SetAccessibleAction (file:/D:/WORK/apache-maven-REPO/com/baomidou/mybatis-plus-core/3.5.3.1/mybatis-plus-core-3.5.3.1.jar) to field java.lang.invoke.SerializedLambda.capturingClass
WARNING: Please consider reporting this to the maintainers of com.baomidou.mybatisplus.core.toolkit.SetAccessibleAction
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@7ad2c4a1] will not be managed by Spring
==>  Preparing: SELECT id,user_id,amount FROM t_account WHERE (user_id = ?)
==> Parameters: 1(String)
<==    Columns: id, user_id, amount
<==        Row: 1, 1, 4000.0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@46fda57c]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435cfb65] was not registered for synchronization because synchronization is not active
JDBC Connection [io.seata.rm.datasource.ConnectionProxy@45e34ff] will not be managed by Spring
original SQL: UPDATE t_account  SET user_id=?,
amount=?  WHERE id=?
SQL to parse, SQL: UPDATE t_account  SET user_id=?,
amount=?  WHERE id=?
parse the finished SQL: UPDATE t_account SET user_id = ?, amount = ? WHERE id = ?
==>  Preparing: UPDATE t_account SET user_id=?, amount=? WHERE id=?
==> Parameters: 1(String), 0.0(BigDecimal), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@435cfb65]
2023-06-12 18:29:07.174  INFO 8076 --- [h_RMROLE_1_1_32] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=192.168.50.15:8091:18416337030565889,branchId=18416337030565890,branchType=AT,resourceId=jdbc:mysql://192.168.50.15:3306/seata_account,applicationData=null
2023-06-12 18:29:07.176  INFO 8076 --- [h_RMROLE_1_1_32] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 192.168.50.15:8091:18416337030565889 18416337030565890 jdbc:mysql://192.168.50.15:3306/seata_account
2023-06-12 18:29:07.207  INFO 8076 --- [h_RMROLE_1_1_32] i.s.r.d.undo.AbstractUndoLogManager      : xid 192.168.50.15:8091:18416337030565889 branch 18416337030565890, undo_log deleted with GlobalFinished
2023-06-12 18:29:07.208  INFO 8076 --- [h_RMROLE_1_1_32] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked

order 服务日志如下 报了库存不足Not enough stock的异常

JDBC Connection [io.seata.rm.datasource.ConnectionProxy@51d520a6] will not be managed by Spring
==>  Preparing: SELECT id,name,commodity_code,count FROM t_stock WHERE (commodity_code = ?)
==> Parameters: C202306121800(String)
<==    Columns: id, name, commodity_code, count
<==        Row: 1, 水杯, C202306121800, 1000
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47dd5002]
2023-06-12 18:29:07.139 ERROR 34704 --- [p-nio-83-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: Not enough stock] with root cause

java.lang.RuntimeException: Not enough stock
	at com.blankhang.stock.service.impl.StockServiceImpl.deduce(StockServiceImpl.java:42) ~[classes/:na]
	at com.blankhang.stock.service.impl.StockServiceImpl$$FastClassBySpringCGLIB$$f693087.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.23.jar:5.3.23]
	...

seata-server 日志如下

seata-seata-server-1  | 18:29:06.381  INFO --- [     batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler         : timeout=60000,transactionName=toOrder(java.lang.String, java.lang.String, java.lang.Integer, java.math.BigDecimal),clientIp:192.168.50.2,vgroup:default_tx_group
seata-seata-server-1  | 18:29:06.408  INFO --- [rverHandlerThread_1_5_500] i.s.s.coordinator.DefaultCoordinator     : Begin new global transaction applicationId: business,transactionServiceGroup: default_tx_group, transactionName: toOrder(java.lang.String, java.lang.String, java.lang.Integer, java.math.BigDecimal),timeout:60000,xid:192.168.50.15:8091:18416337030565889
seata-seata-server-1  | 18:29:06.835  INFO --- [     batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler         : SeataMergeMessage xid=192.168.50.15:8091:18416337030565889,branchType=AT,resourceId=jdbc:mysql://192.168.50.15:3306/seata_account,lockKey=t_account:1
seata-seata-server-1  | ,clientIp:192.168.50.2,vgroup:default_tx_group
seata-seata-server-1  | 18:29:06.857  INFO --- [rverHandlerThread_1_6_500] i.seata.server.coordinator.AbstractCore  : Register branch successfully, xid = 192.168.50.15:8091:18416337030565889, branchId = 18416337030565890, resourceId = jdbc:mysql://192.168.50.15:3306/seata_account ,lockKeys = t_account:1
seata-seata-server-1  | 18:29:07.196  INFO --- [     batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler         : xid=192.168.50.15:8091:18416337030565889,extraData=null,clientIp:192.168.50.2,vgroup:default_tx_group
seata-seata-server-1  | 18:29:07.240  INFO --- [rverHandlerThread_1_7_500] io.seata.server.coordinator.DefaultCore  : Rollback branch transaction successfully, xid = 192.168.50.15:8091:18416337030565889 branchId = 18416337030565890
seata-seata-server-1  | 18:29:07.241  INFO --- [rverHandlerThread_1_7_500] io.seata.server.coordinator.DefaultCore  : Rollback global transaction successfully, xid = 192.168.50.15:8091:18416337030565889.

请求后 DB 数据如下

t_account 表数据

id user_id account
1 1 4000

t_account 表数据

id commodity_code name count
1 C202306121800 水杯 1000

t_order 表无数据生成

通过服务日志、seata-server日志、db 可以发现,seata提示事务全局回滚成功, db 数据未修改,到此分布式事务完成。
在使用seata后 如果要使用分布式事务,只需要在入口方法中加上@GlobalTransactional注解即可。

如果要结合本地事务一起使用的话,本地的事务只能被@GlobalTransactional注解内的方法包含, 否则会导致分布式事务失败!
下面是正确用法

public class test {
    
    @Resource
    private FeignA feignA;
    @Resource
    private FeignB feignB;
    
    @GlobalTransactional
    public void startGlobalTX(){
        feignA.doSomething();
        txLocal();
        feignB.doSomething();
    }
    
    @Transactional
    public void txLocal(){
       System.out.println('local tx...');
    }
}

下面是错误用法

public class test {
    
    @Resource
    private FeignA feignA;
    @Resource
    private FeignB feignB;
    
    
    @Transactional
    public void txLocal(){
       startGlobalTX();
       System.out.println('local tx...');
    }
    
    @GlobalTransactional
    public void startGlobalTX(){
        feignA.doSomething();
        feignB.doSomething();
    }
}

代码在github

参考来源

Seata安装与使用 https://www.cnblogs.com/wt20/p/17158267.html
docker-compose 部署 Seata Server
官方文档

seata-nacos-cloud's People

Contributors

blankhang avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.