接上篇:sptingboot 整合 seata1.6.1 (AT模式)
简介 官方介绍
TCC 模式,不依赖于底层数据资源的事务支持:
一阶段prepare
行为:调用 自定义 的prepare
逻辑。
二阶段commit
行为:调用 自定义 的commit
逻辑。
二阶段rollback
行为:调用 自定义 的rollback
逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的
- 一阶段
prepare
行为 - 二阶段
commit
或rollback
行为
过程
- 上一篇文章项目copy一份命名
lagou_parent_seata_tcc
- 修改父工程依赖、.iml 文件 、.pom文件
- 修改各个(business\order\point\storage)子工程的启动类
//修改前
@SpringBootApplication(scanBasePackages = "com.lagou",exclude = DataSourceAutoConfiguration.class)
//修改后
@SpringBootApplication
- …_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
- 以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
参数表示全局事务注解名称,commitMethod
和rollbackMethod
,分别表示提交和回滚的通知方法。
@BusinessActionContextParameter
注解,被标注的参数可以直接放在actionContext中
- 启动服务
-
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]