当前位置:网站首页>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》
边栏推荐
- Summary of stack frame in arm assembly
- Difference between left join on and join on
- [proteus simulation] Arduino uno+ relay controls lighting equipment
- Unity存档系统——Json格式的文件
- Insurance app aging service evaluation analysis 2022 issue 06
- AOSP ~ WIFI架构总览
- 202112-2 序列查询新解
- 同花顺证券开户安全吗
- AOSP ~ 默认属性值
- Two way combination of business and technology to build a bank data security management system
猜你喜欢

leecode学习笔记-机器人走到终点的最短路径

AI clothing generation helps you complete the last step of clothing design

Single case of hungry and lazy mode

EasyNVR使用Onvif探测设备失败,显示“无数据”是什么原因?

AOSP ~ WIFI架构总览

Two way combination of business and technology to build a bank data security management system

Pytorch learning notes (VII) ------------------ vision transformer

20 years ICPC Macau station L - random permutation
![Network planning | [four network layers] knowledge points and examples](/img/c3/d7f382409e99eeee4dcf4f50f1a259.png)
Network planning | [four network layers] knowledge points and examples

Seata四大模式之TCC模式详解及代码实现
随机推荐
2022年海外电商运营三大关键讲解
DSPACE set zebra crossings and road arrows
数组-一口气冲完快慢指针
ERROR日志格式与注意点
Is it safe for Guoxin golden sun to open an account in the steps of opening new bonds
Planification du réseau | [quatre couches de réseau] points de connaissance et exemples
Migrate Oracle database from windows system to Linux Oracle RAC cluster environment (4) -- modify the scanip of Oracle11g RAC cluster
Charles packet capturing tool
20年ICPC澳门站L - Random Permutation
Difference between left join on and join on
请问polarDB数据库可以通过mysql进行数据源连接吗
保险也能拼购?个人可以凑够人数组团购买医疗保险的4大风险
MATLAB主窗口与编辑器窗口分开为两个界面的解决办法
爱
How to uninstall CUDA
DSPACE的性能渲染问题
計網 | 【四 網絡層】知識點及例題
打新债是不是骗局 开户是安全的吗
Performance rendering of dSPACE
The Oracle 11g RAC cluster database cannot be started due to directory permission errors