前回の記事からの続き:sptingbootがseata1.6.1を統合(ATモード)
はじめに公式紹介
TCCモード、基礎となるデータ リソースに依存しないトランザクション サポート:
1 フェーズprepare
動作:カスタムprepare
ロジックの呼び出し。
フェーズ 2 のcommit
動作:カスタムロジックを呼び出しますcommit
。
フェーズ 2 のrollback
動作:カスタムロジックを呼び出しますrollback
。
いわゆる TCC モードは、カスタマイズされたブランチ トランザクションのグローバル トランザクション管理への統合のサポートを指します。
分散グローバル トランザクションは 2 フェーズ コミット モデルです。グローバル トランザクションは複数のブランチ トランザクションで構成されます。ブランチ トランザクションは 2 段階コミット モデルの要件を満たす必要があります。つまり、各ブランチ トランザクションは独自のトランザクションを持つ必要があります。
- ステージ 1 の
prepare
動作 - ステージ 2
commit
またはrollback
行動
プロセス
- 前の記事 プロジェクトのコピーと命名
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
- 注文プロジェクトを例として、準備、コミット、ロールバックのロジックをカスタマイズします。
- \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 では、 TCC モードでのビジネス処理の方法を示すため
に追加する必要もあります。ここで、パラメーターはグローバル トランザクション アノテーションの名前を示し、送信とロールバックの通知方法をそれぞれ示します。注釈。注釈付きパラメータは actionContext に直接配置できます。@GlobalTransactional
@LocalTCC
@TwoPhaseBusinessAction
name
commitMethod
rollbackMethod
@BusinessActionContextParameter
- サービス開始
-
ナコス
-
シータサーバー
-
アイデアプロジェクト
確認する
在庫は 100 です。注文が 100 を超えると、分散トランザクションのロールバックがトリガーされます。
http://localhost:8000/test1 通常通りに注文してポイントを加算し、在庫を減らします。
http://localhost:8000/test2 異常な発注、在庫不足、注文とポイントのロールバック。
- 症例リンク
http://localhost:8000/test1
通常通りに注文し、ポイントを追加し、在庫を減らします。
http://localhost:8000/test2
異常な注文、在庫不足、注文とポイントのロールバック。
質問
問題を確認したところ、seata バージョン 1.5.1 以降では
TCC
冪等制御、ハング防止、空のロールバックがサポートされていることがわかりました。準備フェーズがインターフェイスに追加され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]