当前位置:网站首页>TCC mode explanation and code implementation of Seata's four modes
TCC mode explanation and code implementation of Seata's four modes
2022-06-25 03:06:00 【In a flash】
Catalog
1、 Implementation mechanism
1.1 Submission phase
TCC A pattern is a distributed transaction solution that needs to be encoded in business code .
- A stage :Try, Detect and reserve resources .
- Two stages :
- Submit :Confirm, Complete resource operation business ; requirement Try success Confirm Be sure to succeed .
- Roll back :Cancel, Release reserved resources , It can be understood as try Reverse operation of .
1.2 Implementation logic

A stage :
try, Try to lock the resource , For example, the amount to be deducted , And record a deduction record , Executive business ( Generate orders and other operations ).
Second order - Transaction submission
Delete the deduction record of the amount , Indicates the completion of the entire transaction process .
Second order - Transaction rollback
Obtain deduction records , Recover the amount from the deduction , Indicates data rollback .
TCC Empty rollback and business suspension in :
When a branch transaction is executing Try In operation , Global get status timeout due to blocking , To carry out Cancel operation , In the absence of execution Try The operation was performed Cancel This is called null rollback . When the business of empty rollback is executed, if there is no blocking, it will continue to execute Try operation , Will result in failure to perform subsequent Confirm perhaps Cancel operation , This is business suspension .
1.3 Advantages and disadvantages
advantage :
- Complete the direct commit transaction in one phase , Release database resources , Good performance .
- comparison AT Model , No need to generate snapshots , Global locks are not required , Strong performance .
- Do not rely on database transactions , It depends not on the operation but on the compensation , It can be used in non transactional databases .
shortcoming :
- There's code intrusion , It needs to be written manually try、Confirm and Cancel Interface , More trouble .
- Soft state , Transactions are ultimately consistent .
- You need to consider Confirm and Cancel The failure of , Do idempotent processing .
2、 Code implementation
Create two SpringBoot engineering , Respectively storage-service And order-service, Simulation from on order-service New orders in the service , And then call storage-service Service new inventory deduction record ,TCC It is necessary for developers to implement rollback compensation mechanism by designing code ; The core code is as follows , Refer to the end of the text for the complete code github Address :
2.1 Create table statement
-- Database name : seata-tcc-demo.sql
-- The order sheet
CREATE TABLE `tb_order`
(
`id` int(11) NOT NULL COMMENT ' Primary key ',
`count` int(11) NULL DEFAULT 0 COMMENT ' Order quantity ',
`money` int(11) NULL DEFAULT 0 COMMENT ' amount of money ',
`status` int(11) NULL DEFAULT 1 COMMENT ' state :1: Preprocessing ,2- complete ',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- An inventory statement
CREATE TABLE `tb_storage`
(
`id` int(11) NOT NULL COMMENT ' Primary key ',
`order_id` int(11) NOT NULL COMMENT ' Order ID',
`count` int(11) NOT NULL DEFAULT 0 COMMENT ' stock ',
`status` int(11) NULL DEFAULT 1 COMMENT ' state :1: Preprocessing ,2- complete ',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
2.2 order-service service
2.2.1 yaml To configure
server:
port: 8082
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/seata-at-demo?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
group: test
seata:
enabled: true
application-id: ${
spring.application.name}
# The name of the transaction group , Corresponding service.vgroupMapping.default_tx_group=xxx Configured in default_tx_group
tx-service-group: default_tx_group
# Configure the correspondence between the transaction group and the cluster
service:
vgroup-mapping:
# default_tx_group Is the name of the transaction Group ,default Name the cluster
default_tx_group: default
disable-global-transaction: false
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
cluster: default
config:
type: nacos
nacos:
server-addr: 162.14.115.18:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
data-id: seataServer.properties
2.2.2 Service Interface
Use... On the interface @LocalTCC The comment means open TCC Pattern , otherwise seata Think of it as AT Pattern ;
@LocalTCC
public interface OrderService {
/** * Create order * @TwoPhaseBusinessAction describe ⼆ Two-phase commit * name: by tcc⽅ French bean name , Need global only ⼀,⼀ General writing ⽅ The legal name is enough * commitMethod: Commit⽅ French ⽅ Legal name * rollbackMethod:Rollback⽅ French ⽅ Legal name * @BusinessActionContextParamete The annotation ⽤ To modify Try⽅ French ⼊ ginseng , * Decorated ⼊ Reference can be made in Commit ⽅ Law and Rollback ⽅ Passed in law BusinessActionContext obtain . * @param order * @return */
@TwoPhaseBusinessAction(name = "createOrderPrepare", commitMethod = "createOrderCommit", rollbackMethod = "createOrderRollBack")
Order createOrderPrepare(@BusinessActionContextParameter(paramName = "order") Order order);
/** * Submit * @param context * @return */
Boolean createOrderCommit(BusinessActionContext context);
/** * Roll back * @param context * @return */
Boolean createOrderRollBack(BusinessActionContext context);
}
2.2.3 Service Implementation class
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
private static final Map<String, String> STATUS_MAP = new ConcurrentHashMap<>();
@Resource
private OrderMapper orderMapper;
/** * Create order * * @param order * @return */
@Override
public Order createOrderPrepare(Order order) {
// 0. Get transaction id
String xid = RootContext.getXID();
log.info(" Create order pre-processing ,xid={}",xid );
// Set to pre-processing state
order.setStatus(1);
// Judge whether it has been executed Cancel perhaps Confirm
if(STATUS_MAP.get(xid)!=null){
// Indicates that... Has been executed Cancel perhaps Confirm Realize service suspension
return null;
}
orderMapper.insert(order);
return order;
}
/** * Submit * @param context * @return */
@Override
public Boolean createOrderCommit(BusinessActionContext context){
try {
String xid = context.getXid();
// Change the status of the order to complete
log.info(" Create order submission processing ,xid={}",xid );
// Idempotent processing
if(STATUS_MAP.get(xid)!=null){
return true;
}
STATUS_MAP.put(xid,"Confirm");
Object obj = context.getActionContext("order");
if(obj!=null) {
Order order = JSON.parseObject(obj.toString(), Order.class);
if (order != null) {
order.setStatus(2);
orderMapper.updateById(order);
}
}
}catch (Exception e){
log.error(e.getMessage());
}
return true;
}
/** * Roll back * @param context * @return */
@Override
public Boolean createOrderRollBack(BusinessActionContext context){
try {
String xid = context.getXid();
log.info(" Create an order rollback process ,xid={}",xid );
// Idempotent processing
if(STATUS_MAP.get(xid)!=null){
return true;
}
STATUS_MAP.put(xid,"Cancel");
// Change the status of the order to complete
Object obj = context.getActionContext("order");
if(obj!=null) {
Order order = JSON.parseObject(obj.toString(), Order.class);
// Delete the order , Represents a rollback
if (order != null) {
log.info(" Delete order ID:"+order.getId());
orderMapper.deleteById(order.getId());
}
}
}catch (Exception e){
log.error(e.getMessage());
}
return true;
}
}
2.2.4 Controller
@RestController
@RequestMapping("order")
public class OrderController {
@Resource
private TccHandler tccHandler;
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody Order order) {
long id = new Random().nextInt(999999999);
order.setId(id);
tccHandler.createOrderAndStorage(order);
return ResponseEntity.status(HttpStatus.OK).body(" Successful operation ");
}
}
2.2.5 TCC processor
@Component
@Slf4j
public class TccHandler {
@Resource
private OrderService orderService;
@Resource
private StorageClient storageClient;
/** * To create orders and inventory records TCC processor * Use @GlobalTransactional Open global transaction * @param order * @return */
@GlobalTransactional
public void createOrderAndStorage(Order order) {
// Record order data
log.info(" Start recording order data ...");
Order orderPrepare = orderService.createOrderPrepare(order);
log.info(" End recording order data ...");
// feign Call to record inventory data
log.info(" Start recording inventory data ...");
storageClient.deduct(orderPrepare.getId(),orderPrepare.getCount());
log.info(" End recording inventory data ...");
// The simulation results in abnormal conditions
int a=1/0;
}
}
2.2.6 StorageClient
@FeignClient("storage-service")
public interface StorageClient {
/** * Deducting the inventory * * @param orderId * @param count */
@PostMapping("/storage")
void deduct(@RequestParam("orderId") Long orderId, @RequestParam("count") Integer count);
}
2.3 storage-service service
2.3.1 yaml To configure
server:
port: 8081
spring:
application:
name: storage-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/seata-at-demo?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
group: test
# stay dev Environment debug when , You can set the time to be longer
#heart-beat-interval: 1000 # Heartbeat interval . The unit is millisecond , Default 5*1000
heart-beat-timeout: 300000 # Cardiac arrest , No heartbeat , Will set the instance as unhealthy . The unit is millisecond , Default 15*1000
ip-delete-timeout: 4000000 #Ip Delete timeout , No heartbeat , The instance will be deleted . The unit is millisecond , Default 30*1000
seata:
enabled: true
application-id: ${
spring.application.name}
# The name of the transaction group , Corresponding service.vgroupMapping.default_tx_group=xxx Configured in default_tx_group
tx-service-group: default_tx_group
# Configure the correspondence between the transaction group and the cluster
service:
vgroup-mapping:
# default_tx_group Is the name of the transaction Group ,default Name the cluster
default_tx_group: default
disable-global-transaction: false
registry:
type: nacos
nacos:
application: seata-server
server-addr: 162.14.115.18:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
cluster: default
config:
type: nacos
nacos:
server-addr: 162.14.115.18:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
data-id: seataServer.properties
2.3.2 Service Interface
Use... On the interface @LocalTCC The comment means open TCC Pattern , otherwise seata Think of it as AT Pattern ;
@LocalTCC
public interface StorageService {
/** * Create order * @TwoPhaseBusinessAction describe ⼆ Two-phase commit * name: by tcc⽅ French bean name , Need global only ⼀,⼀ General writing ⽅ The legal name is enough * commitMethod: Commit⽅ French ⽅ Legal name * rollbackMethod:Rollback⽅ French ⽅ Legal name * @BusinessActionContextParamete The annotation ⽤ To modify Try⽅ French ⼊ ginseng , * Decorated ⼊ Reference can be made in Commit ⽅ Law and Rollback ⽅ Passed in law BusinessActionContext obtain . * * @param storage * @return */
@TwoPhaseBusinessAction(name = "createPrepare", commitMethod = "deductCommit", rollbackMethod = "deductRollBack")
void deductPrepare(@BusinessActionContextParameter(paramName = "storage") Storage storage);
/** * Submit * @param context * @return */
Boolean deductCommit(BusinessActionContext context);
/** * Roll back * @param context * @return */
Boolean deductRollBack(BusinessActionContext context);
}
2.3.3 Service Implementation class
@Slf4j
@Service
public class StorageServiceImpl implements StorageService {
private static final Map<String, String> STATUS_MAP = new ConcurrentHashMap<>();
@Resource
private StorageMapper storageMapper;
/** * Deduct the amount of storage * */
@Override
public void deductPrepare( Storage storage) {
// 0. Get transaction id
String xid = RootContext.getXID();
log.info(" Record inventory information preprocessing ,xid={}",xid );
try {
// Set to pre-processing state
storage.setStatus(1);
// Judge whether it has been executed Cancel perhaps Confirm
if(STATUS_MAP.get(xid)!=null){
// Indicates that... Has been executed Cancel perhaps Confirm Realize service suspension
return ;
}
storageMapper.insert(storage);
// The downstream service throws an exception
// int a = 1 / 0;
} catch (Exception e) {
throw new RuntimeException(" Inventory deduction failed , It may be that the inventory is insufficient !", e);
}
log.info(" Inventory information recorded successfully ");
}
/** * Submit * @param context * @return */
@Override
public Boolean deductCommit(BusinessActionContext context){
try {
String xid = context.getXid();
// Change the status to complete
log.info(" Record inventory information and submit it for processing ,xid={}", xid);
// Idempotent processing
if(STATUS_MAP.get(xid)!=null){
return true;
}
STATUS_MAP.put(xid,"Confirm");
Object obj = context.getActionContext("storage");
if (obj != null) {
Storage storage = JSON.parseObject(obj.toString(), Storage.class);
if (storage != null) {
storage.setStatus(2);
storageMapper.updateById(storage);
}
}
}catch (Exception e){
log.error(e.getMessage());
}
return true;
}
/** * Roll back * @param context * @return */
@Override
public Boolean deductRollBack(BusinessActionContext context){
try {
String xid = context.getXid();
log.info(" Record inventory information rollback ,xid={}",xid );
// Idempotent processing
if(STATUS_MAP.get(xid)!=null){
return true;
}
STATUS_MAP.put(xid,"Cancel");
// Change the status of the order to complete
Object obj = context.getActionContext("storage");
if(obj!=null) {
Storage storage = JSON.parseObject(obj.toString(), Storage.class);
if (storage != null) {
// Delete the record , Represents a rollback
log.info(" Delete record ID:"+storage.getId());
storageMapper.deleteById(storage.getId());
}
}
}catch (Exception e){
log.error(e.getMessage());
}
return true;
}
}
2.3.4 Controller
@RestController
@RequestMapping("storage")
public class StorageController {
@Resource
private StorageService storageService;
/** * Deducting the inventory * * @param orderId goods ID * @param count The amount to be deducted * @return */
@PostMapping
public ResponseEntity<Void> deduct(@RequestParam("orderId") Long orderId, @RequestParam("count") Integer count) {
Storage storage = new Storage();
long id = new Random().nextInt(999999999);
storage.setId(id);
storage.setOrderId(orderId);
storage.setCount(count);
storageService.deductPrepare(storage);
return ResponseEntity.status(HttpStatus.OK).body(null);
}
}
Be careful : TCC Is to write custom code manually , Rollback and commit transactions , The global transaction is controlled by seata complete
3 test
There is no screenshot for demonstration during the test , It only shows the result , You can run code to set exceptions to verify
3.1 Downstream service is abnormal
stay order-service The service is normal , stay storage-service Service service Exception thrown in , Observe whether the data is successfully rolled back ; If tb_order And tb_storage There is no data , Indicates that the global transaction is successful ;
3.2 The upstream service is abnormal
order-service Service TccHandler In the implementation of storageClient.deduct() Method to throw an exception , stay storage-service The service is normal , Observe whether the data is successfully rolled back ; If tb_order And tb_storage There is no data , Indicates that the global transaction is successful ;
3.3 Final data consistency verification
We can complete the upstream service storageClient.deduct() Enter the breakpoint immediately after , The test will find that tb_order、tb_storage There's data in , The re release breakpoint makes the program execute abnormally , If you look at the database again, you will find tb_order、tb_storage The data in has been deleted ;
4、 Source code address
Seata value AT Pattern code implementation :《seata-tcc-demo》
边栏推荐
- EasyNVR使用Onvif探测设备失败,显示“无数据”是什么原因?
- Introduction to CUDA Programming minimalist tutorial
- ACM. HJ70 矩阵乘法计算量估算 ●●
- Is flush a regular platform? Is it safe for flush to open an account
- 用指南针开户如何选择证券公司?哪一个是更安全的
- Migrate Oracle database from windows system to Linux Oracle RAC cluster environment (1) -- migrate data to node 1
- 数组-一口气冲完快慢指针
- Tell you about mvcc sequel
- Getting started with unityshader Essentials - PBS physics based rendering
- PyTorch学习笔记(七)------------------ Vision Transformer
猜你喜欢

XML modeling

mysql学习笔记--单张表上的增删改查
![[FPGA] serial port controls temperature acquisition by command](/img/63/19b909437b59867c577b39b1138f8f.png)
[FPGA] serial port controls temperature acquisition by command

Performance rendering of dSPACE

小米路由R4A千兆版安装breed+OpenWRT教程(全脚本无需硬改)

Please check the list of commonly used software testing tools.

Refresh mechanism of vie

Insurance can also be bought together? Four risks that individuals can pool enough people to buy Medical Insurance in groups

計網 | 【四 網絡層】知識點及例題

PyTorch学习笔记(七)------------------ Vision Transformer
随机推荐
Wechat applet obtains the parameters carried after scanning the QR code
记一次beego通过go get命令后找不到bee.exe的坑
Charles 抓包工具
@PostConstruct
LeetCode 210:课程表 II (拓扑排序)
好用的字典-defaultdict
李宏毅《机器学习》丨6. Convolutional Neural Network(卷积神经网络)
Introduction to database system
Advanced usage of groovy
MySQL installation tutorial
Error log format and precautions
ERROR日志格式与注意点
There is the word "Internet" in the concept of industrial Internet, but it is an existence that is not related to the Internet
14 bs对象.节点名称.name attrs string 获取节点名称 属性 内容
Two way combination of business and technology to build a bank data security management system
When people look at the industrial Internet from the Internet like thinking and perspective, they have actually fallen into a dead end
Difference between left join on and join on
高数 | 精通中值定理 解题套路汇总
Easy to use dictionary -defaultdict
Egg 服务搭建微信公众号的基础服务