分布式事务Seata-AT模式

yishuai10 · 2024-10-30 23:10:36 · 66 次点击

1. seata安装



  1. docker 安装


docker run --name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-e SEATA_IP=192.168.0.250 \
-e SEATA_PORT=8091 \
seataio/seata-server


  1. 将安装好的配置文件数据,拷贝一份到物理机


docker cp seata-serve:/seata-server/resources /User/seata/config


  1. 修改配置文件


server:
port: 7091

spring:
application:
name: seata-server

logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${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.0.250:8848
namespace:
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: seataServer.properties
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: 192.168.0.250:8848
group: SEATA_GROUP
namespace:
cluster: default
username: nacos
password: nacos
context-path:
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.0.250:3306/seata-work?rewriteBatchedStatements=true # 配置数据库服务端地址,提前初始化SQL
user: root
password: mysql_xMYB44
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
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login


  1. 删除旧的seata-server,重新安装,并进行路径映射


docker run --name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-e SEATA_IP=192.168.0.250 \
-e SEATA_PORT=8091 \
-v /User/seata/config:/seata-server/resources \
seataio/seata-server

2. 数据库初始化



  1. 客户端


LS0g5rOo5oSP5q2k5aSEMC4zLjArIOWinuWKoOWUr+S4gOe0ouW8lSB1eF91bmRvX2xvZwpDUkVBVEUgVEFCTEUgYHVuZG9fbG9nYCAoCiAgYGlkYCBiaWdpbnQoMjApIE5PVCBOVUxMIEFVVE9fSU5DUkVNRU5ULAogIGBicmFuY2hfaWRgIGJpZ2ludCgyMCkgTk9UIE5VTEwsCiAgYHhpZGAgdmFyY2hhcigxMDApIE5PVCBOVUxMLAogIGBjb250ZXh0YCB2YXJjaGFyKDEyOCkgTk9UIE5VTEwsCiAgYHJvbGxiYWNrX2luZm9gIGxvbmdibG9iIE5PVCBOVUxMLAogIGBsb2dfc3RhdHVzYCBpbnQoMTEpIE5PVCBOVUxMLAogIGBsb2dfY3JlYXRlZGAgZGF0ZXRpbWUgTk9UIE5VTEwsCiAgYGxvZ19tb2RpZmllZGAgZGF0ZXRpbWUgTk9UIE5VTEwsCiAgYGV4dGAgdmFyY2hhcigxMDApIERFRkFVTFQgTlVMTCwKICBQUklNQVJZIEtFWSAoYGlkYCksCiAgVU5JUVVFIEtFWSBgdXhfdW5kb19sb2dgIChgeGlkYCxgYnJhbmNoX2lkYCkKKSBFTkdJTkU9SW5ub0RCIEFVVE9fSU5DUkVNRU5UPTEgREVGQVVMVCBDSEFSU0VUPXV0Zjg7


  1. 服务端


LS0gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gVGhlIHNjcmlwdCB1c2VkIHdoZW4gc3RvcmVNb2RlIGlzICdkYicgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLS0gdGhlIHRhYmxlIHRvIHN0b3JlIEdsb2JhbFNlc3Npb24gZGF0YQpDUkVBVEUgVEFCTEUgSUYgTk9UIEVYSVNUUyBgZ2xvYmFsX3RhYmxlYAooCiAgICBgeGlkYCAgICAgICAgICAgICAgICAgICAgICAgVkFSQ0hBUigxMjgpIE5PVCBOVUxMLAogICAgYHRyYW5zYWN0aW9uX2lkYCAgICAgICAgICAgIEJJR0lOVCwKICAgIGBzdGF0dXNgICAgICAgICAgICAgICAgICAgICBUSU5ZSU5UICAgICAgTk9UIE5VTEwsCiAgICBgYXBwbGljYXRpb25faWRgICAgICAgICAgICAgVkFSQ0hBUigzMiksCiAgICBgdHJhbnNhY3Rpb25fc2VydmljZV9ncm91cGAgVkFSQ0hBUigzMiksCiAgICBgdHJhbnNhY3Rpb25fbmFtZWAgICAgICAgICAgVkFSQ0hBUigxMjgpLAogICAgYHRpbWVvdXRgICAgICAgICAgICAgICAgICAgIElOVCwKICAgIGBiZWdpbl90aW1lYCAgICAgICAgICAgICAgICBCSUdJTlQsCiAgICBgYXBwbGljYXRpb25fZGF0YWAgICAgICAgICAgVkFSQ0hBUigyMDAwKSwKICAgIGBnbXRfY3JlYXRlYCAgICAgICAgICAgICAgICBEQVRFVElNRSwKICAgIGBnbXRfbW9kaWZpZWRgICAgICAgICAgICAgICBEQVRFVElNRSwKICAgIFBSSU1BUlkgS0VZIChgeGlkYCksCiAgICBLRVkgYGlkeF9zdGF0dXNfZ210X21vZGlmaWVkYCAoYHN0YXR1c2AgLCBgZ210X21vZGlmaWVkYCksCiAgICBLRVkgYGlkeF90cmFuc2FjdGlvbl9pZGAgKGB0cmFuc2FjdGlvbl9pZGApCikgRU5HSU5FID0gSW5ub0RCCiAgREVGQVVMVCBDSEFSU0VUID0gdXRmOG1iNDsKCi0tIHRoZSB0YWJsZSB0byBzdG9yZSBCcmFuY2hTZXNzaW9uIGRhdGEKQ1JFQVRFIFRBQkxFIElGIE5PVCBFWElTVFMgYGJyYW5jaF90YWJsZWAKKAogICAgYGJyYW5jaF9pZGAgICAgICAgICBCSUdJTlQgICAgICAgTk9UIE5VTEwsCiAgICBgeGlkYCAgICAgICAgICAgICAgIFZBUkNIQVIoMTI4KSBOT1QgTlVMTCwKICAgIGB0cmFuc2FjdGlvbl9pZGAgICAgQklHSU5ULAogICAgYHJlc291cmNlX2dyb3VwX2lkYCBWQVJDSEFSKDMyKSwKICAgIGByZXNvdXJjZV9pZGAgICAgICAgVkFSQ0hBUigyNTYpLAogICAgYGJyYW5jaF90eXBlYCAgICAgICBWQVJDSEFSKDgpLAogICAgYHN0YXR1c2AgICAgICAgICAgICBUSU5ZSU5ULAogICAgYGNsaWVudF9pZGAgICAgICAgICBWQVJDSEFSKDY0KSwKICAgIGBhcHBsaWNhdGlvbl9kYXRhYCAgVkFSQ0hBUigyMDAwKSwKICAgIGBnbXRfY3JlYXRlYCAgICAgICAgREFURVRJTUUoNiksCiAgICBgZ210X21vZGlmaWVkYCAgICAgIERBVEVUSU1FKDYpLAogICAgUFJJTUFSWSBLRVkgKGBicmFuY2hfaWRgKSwKICAgIEtFWSBgaWR4X3hpZGAgKGB4aWRgKQopIEVOR0lORSA9IElubm9EQgogIERFRkFVTFQgQ0hBUlNFVCA9IHV0ZjhtYjQ7CgotLSB0aGUgdGFibGUgdG8gc3RvcmUgbG9jayBkYXRhCkNSRUFURSBUQUJMRSBJRiBOT1QgRVhJU1RTIGBsb2NrX3RhYmxlYAooCiAgICBgcm93X2tleWAgICAgICAgIFZBUkNIQVIoMTI4KSBOT1QgTlVMTCwKICAgIGB4aWRgICAgICAgICAgICAgVkFSQ0hBUigxMjgpLAogICAgYHRyYW5zYWN0aW9uX2lkYCBCSUdJTlQsCiAgICBgYnJhbmNoX2lkYCAgICAgIEJJR0lOVCAgICAgICBOT1QgTlVMTCwKICAgIGByZXNvdXJjZV9pZGAgICAgVkFSQ0hBUigyNTYpLAogICAgYHRhYmxlX25hbWVgICAgICBWQVJDSEFSKDMyKSwKICAgIGBwa2AgICAgICAgICAgICAgVkFSQ0hBUigzNiksCiAgICBgc3RhdHVzYCAgICAgICAgIFRJTllJTlQgICAgICBOT1QgTlVMTCBERUZBVUxUICcwJyBDT01NRU5UICcwOmxvY2tlZCAsMTpyb2xsYmFja2luZycsCiAgICBgZ210X2NyZWF0ZWAgICAgIERBVEVUSU1FLAogICAgYGdtdF9tb2RpZmllZGAgICBEQVRFVElNRSwKICAgIFBSSU1BUlkgS0VZIChgcm93X2tleWApLAogICAgS0VZIGBpZHhfc3RhdHVzYCAoYHN0YXR1c2ApLAogICAgS0VZIGBpZHhfYnJhbmNoX2lkYCAoYGJyYW5jaF9pZGApLAogICAgS0VZIGBpZHhfeGlkYCAoYHhpZGApCikgRU5HSU5FID0gSW5ub0RCCiAgREVGQVVMVCBDSEFSU0VUID0gdXRmOG1iNDsKCkNSRUFURSBUQUJMRSBJRiBOT1QgRVhJU1RTIGBkaXN0cmlidXRlZF9sb2NrYAooCiAgICBgbG9ja19rZXlgICAgICAgIENIQVIoMjApIE5PVCBOVUxMLAogICAgYGxvY2tfdmFsdWVgICAgICBWQVJDSEFSKDIwKSBOT1QgTlVMTCwKICAgIGBleHBpcmVgICAgICAgICAgQklHSU5ULAogICAgcHJpbWFyeSBrZXkgKGBsb2NrX2tleWApCikgRU5HSU5FID0gSW5ub0RCCiAgREVGQVVMVCBDSEFSU0VUID0gdXRmOG1iNDsKCklOU0VSVCBJTlRPIGBkaXN0cmlidXRlZF9sb2NrYCAobG9ja19rZXksIGxvY2tfdmFsdWUsIGV4cGlyZSkgVkFMVUVTICgnQXN5bmNDb21taXR0aW5nJywgJyAnLCAwKTsKSU5TRVJUIElOVE8gYGRpc3RyaWJ1dGVkX2xvY2tgIChsb2NrX2tleSwgbG9ja192YWx1ZSwgZXhwaXJlKSBWQUxVRVMgKCdSZXRyeUNvbW1pdHRpbmcnLCAnICcsIDApOwpJTlNFUlQgSU5UTyBgZGlzdHJpYnV0ZWRfbG9ja2AgKGxvY2tfa2V5LCBsb2NrX3ZhbHVlLCBleHBpcmUpIFZBTFVFUyAoJ1JldHJ5Um9sbGJhY2tpbmcnLCAnICcsIDApOwpJTlNFUlQgSU5UTyBgZGlzdHJpYnV0ZWRfbG9ja2AgKGxvY2tfa2V5LCBsb2NrX3ZhbHVlLCBleHBpcmUpIFZBTFVFUyAoJ1R4VGltZW91dENoZWNrJywgJyAnLCAwKTs=

3. Pom配置


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

<!-- 若版本加载不出来,则单独添加 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>

4. seata配置


# 配置seata
seata:
enabled: true
tx-service-group: xiaoqiu_tx_group # 事务组
service:
vgroup-mapping:
xiaoqiu_tx_group: SEATA_GROUP # 事务组映射
grouplist:
SEATA_GROUP: 192.168.0.250:8091 # seata ip地址
config:
nacos:
server-addr: 192.168.0.250:8848
username: nacos
password: nacos
registry:
nacos:
server-addr: 192.168.0.250:8848
username: nacos
password: nacos

5. 自动回滚


直接把原有的@Transactional替换成@GlobalTransactional即可


1. 当**本机发生异常,且未被全局异常处理器捕获时**,即可直接回滚;
2. 当**外部调用发生异常时,返回的状态码非200**,也可以直接回滚。

6. 手动回滚




  1. 外部异常,但返回状态码还是200


    调用外部接口时,外部接口自己捕获了异常,此时返回的http状态码为200,不会自动回滚,需要根据返回的数据单独判断,并进行手动回滚。


    举例:


    // 初始化简历
    R<Void> r = workResumeServiceFeign.init(user.getId());
    // 如果调用状态不是200,则手动回滚全局事务
    if (r.getStatus() != HttpStatusEnum.SUCCESS.getCode()) {
    String xid = RootContext.getXID();
    if (StringUtils.isNotBlank(xid)) {
    try {
    GlobalTransactionContext.reload(xid).rollback();
    } catch (TransactionException e) {
    log.error("createUsers, 回滚全局事务失败! mobile: {}", mobile, e);
    } finally {
    GraceException.display(ResponseStatusEnum.USER_REGISTER_ERROR);
    }
    }
    }



  2. 本地异常,但异常被全局异常捕获


    此时本地发生了异常,但是由于优雅处理异常的全局异常处理类,把异常吃掉了,所以需要单独处理。此时可以加一个AOP切面,对方法进行包装,手动的拦截要全局事务包裹的类,然后手动回滚异常。


    举例:


    异常代码:


    		@GlobalTransactional
    @Override
    public Users createUsers(String mobile) {
    Users user = getDefaultUsers(mobile);
    user.setMobile(mobile);
    usersMapper.insert(user);

    // 初始化简历
    workResumeServiceFeign.init(user.getId());
    // 模拟除0异常
    int a = 1 / 0;
    return user;
    }

    切面:


    @Slf4j
    @Component
    @Aspect
    public class SeataTransactionAspect {

    /**
    * 调用service之前,手动加入或者创建全局事务
    */
    @Before("execution(* com.xiaoqiu.service.impl.UsersServiceImpl.createUsers(..))")
    public void beginTransaction(JoinPoint joinPoint) throws TransactionException {
    log.info("手动开启全局事务");
    // 手动开启全局事务
    GlobalTransaction gt = GlobalTransactionContext.getCurrentOrCreate();
    gt.begin();
    }

    /**
    * 捕获异常,则手动回滚全局事务
    */
    @AfterThrowing(
    throwing = "throwable",
    pointcut = "execution(* com.xiaoqiu.service.impl.UsersServiceImpl.createUsers(..))"
    )
    public void seataRollback(Throwable throwable) throws Throwable {
    log.warn("捕获到异常信息,则回滚,异常信息为:{}", throwable.getMessage());

    // 从当前线程获得xid
    String xid = RootContext.getXID();
    if (StringUtils.isNotBlank(xid)) {
    GlobalTransactionContext.reload(xid).rollback();
    }
    }
    }


举报· 66 次点击
登录 注册 站外分享
4 条回复  
Chat 初学 2024-10-30 23:10:36

这个有个坑,业务链过长,会导致自动回滚

WyInnovate 初学 2024-10-30 23:20:13

看不懂,先收藏一下

返回顶部