springcloud-seata分布式事务

时间:2024-02-23 11:09:42

seata安装配置

下载

下载中心 | Seata

image-20231210180818159

image-20231210180747873

上传到linux

image-20231210180957167

解压seata压缩包

tar -zxvf seata-server-1.6.1.tar.gz

image-20231210181100220

nacos配置

创建seata的命名空间

id: seata-id

image-20231210182019152

image-20231210182116522

配置seata

备份application.yml

进入/usr/local/software/springcloud/seata/conf文件夹

备份application.yml

mv application.yml application.yml.bak

image-20231210181542597

修改application.example.yml为application.yml

mv application.example.yml application.yml

image-20231210181718205

修改application.yml

添加seata.security模块
seata:
  #新添加内容,不加会报错
  security:
    secretKey: "securityKey"
    tokenValidityInMilliseconds: 1000000000

image-20231210182328278

配置seata.config模块
 37   config: 
 38     # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
 39     type: nacos
 40     nacos:
 41       server-addr: 192.168.198.128:7777
 42       namespace: seata-id
 43       group: SEATA_GROUP
 44       username:
 45       password:
 46       context-path:
 47       ##if use MSE Nacos with auth, mutex with username/password attribute
 48       #access-key:
 49       #secret-key:
 50       data-id: seataServer.properties

image-20231210182850125

配置seata.registry模块
 72   registry:
 73     # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
 74     type: nacos
 75     # preferred-networks: 30.240.*
 76     nacos:
 77       application: seata-server
 78       server-addr: 192.168.198.128:7777
 79       group: SEATA_GROUP
 80       namespace: seata-id
 81       cluster: default
 82       username:
 83       password:
 84       context-path:
 85       ##if use MSE Nacos with auth, mutex with username/password attribute
 86       #access-key:
 87       #secret-key:

image-20231210182949776

配置seata.store模块

使用redis存储

    redis:
      mode: single
      database: 1
      min-conn: 10
      max-conn: 100
      password:
      max-total: 100
      query-limit: 1000
      single:
        host: 192.168.198.128
        port: 6379
      sentinel:
        master-name:
        sentinel-hosts:
image-20231210212601729
添加console模块

新版本中如果不添加此项会报错。

 32 console:
 33   user:
 34     username: seata
 35     password: 123
image-20231210184034551

修改批处理文件

进入/usr/local/software/springcloud/seata/script/config-center/nacos, 修改nacos-config.sh文件

image-20231210184423344
 53 if [ -z ${tenant} ]; then
 54     tenant="seata-id"
 55 fi
image-20231210184606616

启动脚本:

sh nacos-config.sh -h nacos服务器地址

 sh nacos-config.sh -h 192.168.198.128
image-20231210185123696

image-20231210185231369

启动seata服务

进入/usr/local/software/springcloud/seata/bin文件夹

-rwxr-xr-x. 1 502 games 3779 1216 2022 seata-server.bat
-rwxr-xr-x. 1 502 games 5795 1216 2022 seata-server.sh
-rwxr-xr-x. 1 502 games    0 628 2022 startup.sh
[root@localhost bin]# pwd
/usr/local/software/springcloud/seata/bin
image-20231210184818711

./seata-server.sh

[root@localhost bin]# ./seata-server.sh
apm-skywalking not enabled
seata-server is starting, you can check the /usr/local/software/springcloud/seata/logs/start.out
[root@localhost bin]#

image-20231210185428779

image-20231210185500289

springcloud整合seata

数据库设计

image-20231210231451838image-20231210231509083

创建undo表,放入要执行分布式事务的数据库

CREATE TABLE `undo_log` (
  `branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT transaction mode undo table';

引入依赖

  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
      <exclusions>
          <exclusion>
              <groupId>io.seata</groupId>
              <artifactId>seata-all</artifactId>
          </exclusion>
          <exclusion>
              <groupId>io.seata</groupId>
              <artifactId>seata-spring-boot-starter</artifactId>
          </exclusion>
      </exclusions>
  </dependency>
  <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-all</artifactId>
      <version>1.6.1</version>
  </dependency>
  <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-spring-boot-starter</artifactId>
      <version>1.6.1</version>
  </dependency>

bootstrap.yml配置文件

image-20231210231817158

seata:
  enabled: true
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
    grouplist:
      default: 192.168.198.128:8091
#已经在nacos中配置了,不需要再次配置
#  config:
#    nacos:
#      server-addr: ${spring.cloud.nacos.config.server-addr}
#      namespace: seat-id
#      group: SEATA_GROUP
#  registry:
#    nacos:
#      application: seata-service
#      server-addr: ${spring.cloud.nacos.config.server-addr}
#      namespace: seat-id
#      group: SEATA_GROUP

image-20231210232118677

image-20231210232304602

订单模块

订单模块添加分布式事务

启动订单模块

主从TM
2023-12-10 23:24:28.728  INFO 13176 --- [           main] i.s.core.rpc.netty.NettyPoolableFactory  : NettyPool create channel to transactionRole:TMROLE,address:192.168.198.128:8091,msg:< RegisterTMRequest{applicationId='ssc-cloud-order', transactionServiceGroup='default_tx_group'} >

image-20231210232611395

image-20231210232726507
初始化资源管理(RM)
image-20231210232927633image-20231210232959179image-20231210233114826

发起事务请求

使用@GlobalTransactional

package com.wnhz.ssc.cloud.order.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.wnhz.ssc.cloud.order.IOrderDao;
import com.wnhz.ssc.cloud.order.service.IOrderService;
import com.wnhz.ssc.cloud.store.feign.IStoreFeign;
import com.wnhz.ssc.domain.entity.po.Order;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private IOrderDao orderDao;
    @Autowired
    private IStoreFeign storeFeign;


    @GlobalTransactional
    @Override
    public Long createOrder(long productId, int num) {
        Order order = new Order();
        order.setProduct(productId);
        order.setQuantity(num);
        order.setCreateBy("test");
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        log.debug("创建order对象成功:{},准备存入数据库....", order);

        orderDao.insert(order);
        log.debug("订单对象存入数据库成功,准备扣库存....");
        storeFeign.decreaseStore(productId, num);
        log.debug("库存更成功,商品号:{},扣库存:{}个", productId,num);
        long orderId = new Snowflake().nextId();
        order.setOrderNum(orderId);
        orderDao.updateById(order);
        log.debug("订单号更新完成:{}", orderId);

        return orderId;

    }
}

Begin new global transaction [172.18.1.1:8091:8269074957769797653]
:
:
rm handle branch rollback process:xid=172.18.1.1:8091:8269074957769797653,branchId=8269074957769797654,branchType=AT,resourceId=jdbc:mysql://192.168.198.128:3306/ssc_db,applicationData={"skipCheckLock":true}
Branch Rollbacking: 172.18.1.1:8091:8269074957769797653 8269074957769797654 jdbc:mysql://192.168.198.128:3306/ssc_db
Branch Rollbacked result: PhaseTwo_Rollbacked
Suspending current transaction, xid = 172.18.1.1:8091:8269074957769797653
[172.18.1.1:8091:8269074957769797653] rollback status: Rollbacked
[172.18.1.1:8091:8269074957769797653] rollback status: Finished

自定义异常捕获

由于分布式调用返回的异常可能为OpenFeign的异常,分布式事务异常捕获失败。

定义切面捕获分布式 事务异常

package com.wnhz.ssc.cloud.order.aop;

import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Aspect
@Component
@Slf4j
public class SeataExceptionAop {

    @Pointcut("@annotation(io.seata.spring.annotation.GlobalTransactional)")
    public void transactionPointCut(){}

    @AfterThrowing(throwing = "e",pointcut = "transactionPointCut()")
    public void  globalTransactionalExceiton(Throwable e) throws TransactionException {
        log.debug("分布式事务异常:{}", e.getMessage());
        String xid = RootContext.getXID();
        if(StringUtils.hasText(xid)){
            log.debug("XID:{}执行回滚操作", xid);
            GlobalTransactionContext.reload(xid).rollback();
            log.debug("事务:{}回滚完成", xid);
            throw new TransactionException("事务处理失败,回滚完成........");
        }
    }
}