seata + nacos 使用

吴书松
吴书松
发布于 2025-05-08 / 42 阅读
1

seata + nacos 使用

seata下载部署

官方下载地址:https://seata.apache.org/

也可以到阿里云盘中下载

修改配置

这里以nacos集群版本举例子,也可以部署本地file的单机版本

server:
  port: 7091

spring:
  application:
    name: seata-server

console:
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      server-addr: 192.168.1.111:8848
      namespace: jm-dev
      group: DEFAULT_GROUP
      username: jm-server
      password: jm-inner-0311.
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
      data-id: jmSeataServer.yml
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.1.111:8848
      group: DEFAULT_GROUP
      namespace: jm-dev
      cluster: default
      username: jm-server
      password: jm-inner-0311.
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:

#  server:
#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3wyyztwj
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
Copy to clipboardErrorCopied

在nacos中创建公用配置文件jmSeataServer.yml

内容:

# jm-seata

seata: 
  store:
    # support: file 、 db 、 redis
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.1.40:3306/pig_seata?rewriteBatchedStatements=true
      user: root
      password: 123456
      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

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_logstashCopy to clipboardErrorCopied

注意,如果是连接的数据库,那么需要提前创建好数据库和对应表

表sql在源码中有,如

启动

访问后台

服务集成seata

这里创建公共包

添加依赖

        <!-- seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>
        <!--  seata kryo 序列化-->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-serializer-kryo</artifactId>
        </dependency>Copy to clipboardErrorCopied

增加配置类,指定seata的配置文件yml

package com.xxx.common.seata.config;

import com.xxx.common.core.factory.YamlPropertySourceFactory;
import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * Seata 配置类
 *
 * @author jm-wss
 * @date 2022/3/29
 */
@PropertySource(value = "classpath:seata-config.yml", factory = YamlPropertySourceFactory.class)
@EnableAutoDataSourceProxy(useJdkProxy = true)
@Configuration(proxyBeanMethods = false)
public class SeataAutoConfiguration {

}
Copy to clipboardErrorCopied

在resource中创建seata-config.yml

seata:
  enabled: true
  tx-service-group: jm_tx_group # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
  client:
    rm-report-success-enable: true
    rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false)
    rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5)
    rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000)
    rm:
      lock:
        lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms)
        lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30)
        lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功)
    tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
    tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
    undo:
      data-validation: true # 二阶段回滚镜像校验(默认true开启)
      log-serialization: kryo # undo序列化方式(默认jackson 不支持 LocalDateTime)
      log-table: undo_log  # 自定义undo表名(默认undo_log)
    log:
      exceptionRate: 100 # 日志异常输出概率(默认100)
    support:
      spring:
        datasource-autoproxy: true
  service:
    vgroup-mapping:
      jm_tx_group: default # TC 集群(必须与seata-server保持一致)
    enable-degrade: false # 降级开关
    disable-global-transaction: false # 禁用全局事务(默认false)
    grouplist:
      default: 192.168.1.239:8091
  transport:
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread
    type: TCP
    server: NIO
    heartbeat: true
    serialization: seata
    compressor: none
    enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true)
  registry:
    file:
      name: file.conf
    type: file
  config:
    file:
      name: file.conf
    type: file
Copy to clipboardErrorCopied

resource中创建spring的自动配置文件

创建路径META-INF.spring 注意,这是两个目录,要一个个创建

创建文件 :org.springframework.boot.autoconfigure.AutoConfiguration.imports

内容:

 com.xxxx.common.seata.config.SeataAutoConfiguration
Copy to clipboardErrorCopied

业务服务引入这个公共模块即可

如:

注意,每个参与事务的数据库都需要创建seata的业务表

-- auto-generated definition
create table undo_log
(
    id            bigint auto_increment
        primary key,
    branch_id     bigint       not null,
    xid           varchar(100) not null,
    context       varchar(128) not null,
    rollback_info longblob     not null,
    log_status    int          not null,
    log_created   datetime     not null,
    log_modified  datetime     not null,
    ext           varchar(100) null,
    constraint ux_undo_log
        unique (xid, branch_id)
)
    charset = utf8
    row_format = DYNAMIC;


测试

package com.xxxxx.iotd.controller;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xxxxx.common.core.constant.SecurityConstants;
import com.xxxxx.common.core.exception.BusinessException;
import com.xxxxx.common.core.util.R;
import com.xxxxx.common.security.annotation.Inner;
import com.xxxxx.iotd.api.feign.RemoteIotdService;
import com.xxxxx.iotd.finance.entity.IotdOrderEntity;
import com.xxxxx.iotd.finance.mapper.IotdOrderMapper;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
@Tag(description = "test" , name = "test" )
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class TestController {

    private final IotdOrderMapper iotdOrderMapper;

    //Feign
    private final RemoteIotdService remoteIotdService;

    @Inner
    @GetMapping(value = "/test1")
    public R<String> test1(@RequestParam(value = "p1")String p1, @RequestParam(value = "p2")String p2){

        iotdOrderMapper.update(new IotdOrderEntity(),new LambdaUpdateWrapper<IotdOrderEntity>()
                .eq(IotdOrderEntity::getOrderCode,"D1914190876176928768R1")
                .set(IotdOrderEntity::getOrderRemarks,p2)
        );

        return R.ok();
    }

    @GlobalTransactional
    @Inner(value = false)
    @GetMapping(value = "/test2")
    public R<String> test2(@RequestParam(value = "p1")String p1, @RequestParam(value = "p2")String p2){

        iotdOrderMapper.update(new IotdOrderEntity(),new LambdaUpdateWrapper<IotdOrderEntity>()
                .eq(IotdOrderEntity::getOrderCode,p1)
                .set(IotdOrderEntity::getOrderRemarks,p2)
        );

        R<String> stringR = remoteIotdService.test1(p1, p2 + "_test1"
                , SecurityConstants.FROM_IN);
        System.out.println(JSONObject.toJSONString(stringR));
        if(!stringR.isSuccess()){
            throw new BusinessException(stringR.getMsg());
        }

        //模拟异常
        if("22222".equals(p2)){
            throw new BusinessException("自定义异常");
        }

        return R.ok();
    }

}
Copy to clipboardErrorCopied

正常请求


数据正常修改

异常请求

数据没有变化,回退了

打断点,看下seata后台数据情况