当前位置:网站首页>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》
边栏推荐
- CUDA编程入门极简教程
- 打新债是不是骗局 开户是安全的吗
- Please check the list of commonly used software testing tools.
- C#实现水晶报表绑定数据并实现打印
- Eggservice builds the basic service of wechat official account
- 股票开户,在手机上开户安全吗?
- PE file infrastructure sorting
- Centos7.3 modifying MySQL default password_ Explain centos7 modifying the password of the specified user in MySQL
- Go synchronization waiting group
- 记一次beego通过go get命令后找不到bee.exe的坑
猜你喜欢

Insurance app aging service evaluation analysis 2022 issue 06

PyTorch学习笔记(七)------------------ Vision Transformer
![Planification du réseau | [quatre couches de réseau] points de connaissance et exemples](/img/c3/d7f382409e99eeee4dcf4f50f1a259.png)
Planification du réseau | [quatre couches de réseau] points de connaissance et exemples

Array - fast and slow pointer in one breath

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

Yarn: unable to load file c:\users\xxx\appdata\roaming\npm\yarn PS1 because running scripts is prohibited on this system

Once beego failed to find bee after passing the go get command Exe's pit

单例的饥饿、懒汉模式案例

自动化测试

什么是SSL证书,拥有一个SSL证书有什么好处?
随机推荐
leecode学习笔记-机器人走到终点的最短路径
Advanced mathematics | proficient in mean value theorem problem solving routines summary
MySQL command backup
Mall project pc--- product details page
什么是SSL证书,拥有一个SSL证书有什么好处?
做自媒体不知道怎样变现?7大变现方法分享
AI clothing generation helps you complete the last step of clothing design
How to click DOM to automatically locate the corresponding code line in vscode
Seata四大模式之TCC模式详解及代码实现
計網 | 【四 網絡層】知識點及例題
股票开户用客户经理发的开户链接安全吗?知道的给说一下吧
MATLAB主窗口与编辑器窗口分开为两个界面的解决办法
doak-cms 文章管理系统 推荐
Summary of stack frame in arm assembly
Insurance can also be bought together? Four risks that individuals can pool enough people to buy Medical Insurance in groups
There is the word "Internet" in the concept of industrial Internet, but it is an existence that is not related to the Internet
数组-一口气冲完快慢指针
MCN institutions are blooming everywhere: bloggers and authors should sign contracts carefully, and the industry is very deep
36岁前亚马逊变性黑客,窃取超1亿人数据被判20年监禁!
用指南针开户如何选择证券公司?哪一个是更安全的