sptingboot 整合 seata1.6.1 (TCC模式)

接上篇:sptingboot 整合 seata1.6.1 (AT模式)

简介 官方介绍

TCC 模式,不依赖于底层数据资源的事务支持:
一阶段 prepare 行为:调用 自定义prepare 逻辑。
二阶段 commit 行为:调用 自定义commit 逻辑。
二阶段 rollback 行为:调用 自定义rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的

  • 一阶段 prepare 行为
  • 二阶段 commitrollback 行为
    在这里插入图片描述

过程

  1. 上一篇文章项目copy一份命名lagou_parent_seata_tcc
  • 修改父工程依赖、.iml 文件 、.pom文件
    在这里插入图片描述
  1. 修改各个(business\order\point\storage)子工程的启动类
//修改前
@SpringBootApplication(scanBasePackages = "com.lagou",exclude = DataSourceAutoConfiguration.class)
//修改后
@SpringBootApplication
  1. …_common_db子工程
  • 注释或者删除DataSourceConfiguration 数据源代理配置类(AT需要、TCC不需要)
  • 配置文件
# staet----------------------------seata服务配置
seata:
  # 切换XA模式
#  enable: true
#  data-source-proxy-mode: TCC
#  enable-auto-data-source-proxy: false
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata-server
      group: SEATA_GROUP
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata-server
      application: seata-server
      group: SEATA_GROUP
      username: nacos
      password: nacos
  service:
    vgroup-mapping:
      default_tx_group: default
    disable-global-transaction: false
    grouplist:
      default: 127.0.0.1:8091
  tx-service-group: default_tx_group
spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: default_tx_group
logging:
  level:
    seata: debug

  1. 以order工程为例,自定义prepare 、commit、rollback 逻辑
  • \point\storage工程同理
  • OrderService.java
@LocalTCC
public interface OrderService extends IService<Order> {
    
    
    @TwoPhaseBusinessAction(name = "addTcc", commitMethod = "addCommit", rollbackMethod = "addRollback")
    public void addOrder(@BusinessActionContextParameter(paramName = "order") Order order);

    public boolean addCommit(BusinessActionContext context);

    public boolean addRollback(BusinessActionContext context);
}
package com.lagou.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lagou.order.entity.Order;
import com.lagou.order.mapper.OrderMapper;
import com.lagou.order.service.OrderService;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    
    

    @Override
    @Transactional
    public void addOrder(Order order) {
    
    
        try {
    
    
            order.setCreateTime(new Date());//设置订单创建时间
            order.setStatus(0); //try 预检查阶段
            this.save(order);//保存订单
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * add
     * @param context
     * @return
     */
    @Override
    public boolean addCommit(BusinessActionContext context) {
    
    
        Order contextOrder = JSON.parseObject(context.getActionContext("order").toString(), Order.class);
        if (null != contextOrder) {
    
    
            contextOrder=this.getById(contextOrder.getId());

            if(null != contextOrder){
    
    
                contextOrder.setStatus(1);
                this.saveOrUpdate(contextOrder);
            }
        }
        log.info("OrderService.addCommit---------->xid:{},提交成功",context.getXid());
        return true;
    }

    @Override
    public boolean addRollback(BusinessActionContext context) {
    
    
        Order contextOrder = JSON.parseObject(context.getActionContext("order").toString(), Order.class);
        if (null != contextOrder) {
    
    
            contextOrder=this.getById(contextOrder.getId());

            if(null != contextOrder){
    
    
                this.removeById(contextOrder.getId());
            }
        }
        log.info("OrderService.addRollback---------->xid:{},回滚成功",context.getXid());
        return true;
    }


}

  • 注解
    TCC 中我们也要加上@GlobalTransactional
    @LocalTCC 表示走TCC模式
    @TwoPhaseBusinessAction表示业务处理的方法,其中 name 参数表示全局事务注解名称,commitMethodrollbackMethod ,分别表示提交和回滚的通知方法。
    @BusinessActionContextParameter注解,被标注的参数可以直接放在actionContext中
  1. 启动服务
  • nacos
    在这里插入图片描述

  • seata-server
    在这里插入图片描述

  • IDEA工程
    在这里插入图片描述

验证

库存为100,下单超过100即可触发分布式事务回滚。
http://localhost:8000/test1 正常下单,加积分,减库存。
http://localhost:8000/test2 异常下单,库存不足,回滚订单和积分。

  • 案例链接
  • http://localhost:8000/test1 正常下单,加积分,减库存。
    在这里插入图片描述
  • http://localhost:8000/test2 异常下单,库存不足,回滚订单和积分。
    在这里插入图片描述

问题

查问题的时候了解到seata 1.5.1版本以上TCC 模式支持幂等控制、防悬挂和空回滚。prepare 阶段在接口上加useTCCFence = true,用到分布式事务的数据库中添加表tcc_fence_log解决。但是我掉坑了…拿掉useTCCFence = true 就不会出现NullPointerException
在这里插入图片描述

@LocalTCC
public interface OrderService extends IService<Order> {
    
    
    @TwoPhaseBusinessAction(name = "addTcc", commitMethod = "addCommit", rollbackMethod = "addRollback",useTCCFence = true)
    public void addOrder(@BusinessActionContextParameter(paramName = "order") Order order);

    public boolean addCommit(BusinessActionContext context);

    public boolean addRollback(BusinessActionContext context);
}
  • 表结构
CREATE TABLE `tcc_fence_log` (
  `xid` varchar(128) NOT NULL COMMENT 'global id',
  `branch_id` bigint(20) NOT NULL COMMENT 'branch id',
  `action_name` varchar(64) NOT NULL COMMENT 'action name',
  `status` tinyint(4) NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',
  `gmt_create` datetime(3) NOT NULL COMMENT 'create time',
  `gmt_modified` datetime(3) NOT NULL COMMENT 'update time',
  PRIMARY KEY (`xid`,`branch_id`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • 问题(本案例遗留此问题)
java.lang.NullPointerException: null
	at io.seata.rm.tcc.TCCFenceHandler.commitFence(TCCFenceHandler.java:146) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.tcc.TCCResourceManager.branchCommit(TCCResourceManager.java:106) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.AbstractRMHandler.doBranchCommit(AbstractRMHandler.java:98) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.AbstractRMHandler$1.execute(AbstractRMHandler.java:54) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.AbstractRMHandler$1.execute(AbstractRMHandler.java:50) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.core.exception.AbstractExceptionHandler.exceptionHandleTemplate(AbstractExceptionHandler.java:131) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.AbstractRMHandler.handle(AbstractRMHandler.java:50) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.DefaultRMHandler.handle(DefaultRMHandler.java:61) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.core.protocol.transaction.BranchCommitRequest.handle(BranchCommitRequest.java:35) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.rm.AbstractRMHandler.onRequest(AbstractRMHandler.java:150) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.core.rpc.processor.client.RmBranchCommitProcessor.handleBranchCommit(RmBranchCommitProcessor.java:63) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.core.rpc.processor.client.RmBranchCommitProcessor.process(RmBranchCommitProcessor.java:58) ~[seata-all-1.6.1.jar:1.6.1]
	at io.seata.core.rpc.netty.AbstractNettyRemoting.lambda$processMessage$2(AbstractNettyRemoting.java:281) ~[seata-all-1.6.1.jar:1.6.1]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-all-4.1.53.Final.jar:4.1.53.Final]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

猜你喜欢

转载自blog.csdn.net/u014535922/article/details/131134208