当前位置:网站首页>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》
边栏推荐
- Advanced usage of groovy
- The Oracle 11g RAC cluster database cannot be started due to directory permission errors
- 14 bs对象.节点名称.name attrs string 获取节点名称 属性 内容
- ACM. Hj75 common substring calculation ●●
- 20年ICPC澳门站L - Random Permutation
- Xiaomi routing R4A Gigabit version installation feed+openwrt tutorial (the full script does not need to be hard modified)
- Insurance app aging service evaluation analysis 2022 issue 06
- Egg 服务搭建微信公众号的基础服务
- AOSP ~ WIFI架构总览
- 打新债100%中签的方法 开户是安全的吗
猜你喜欢
How transformers Roberta adds tokens
Unity archive system - file in JSON format
Yarn: unable to load file c:\users\xxx\appdata\roaming\npm\yarn PS1 because running scripts is prohibited on this system
Getting started with unityshader - Surface Shader
Enlightenment of using shadergraph to make edge fusion particle shader
Unity存档系统——Json格式的文件
保险也能拼购?个人可以凑够人数组团购买医疗保险的4大风险
Before the age of 36, Amazon transgender hackers were sentenced to 20 years' imprisonment for stealing data from more than 100million people!
CUDA编程入门极简教程
mysql学习笔记--单张表上的增删改查
随机推荐
PSQL column to row
Software testing weekly (issue 77): giving up once will breed the habit of giving up, and the problems that could have been solved will become insoluble.
李宏毅《机器学习》丨6. Convolutional Neural Network(卷积神经网络)
Cloud native database vs traditional database
什么是SSL证书,拥有一个SSL证书有什么好处?
保险也能拼购?个人可以凑够人数组团购买医疗保险的4大风险
Yarn: unable to load file c:\users\xxx\appdata\roaming\npm\yarn PS1 because running scripts is prohibited on this system
@PostConstruct
The Oracle 11g RAC cluster database cannot be started due to directory permission errors
Internship: use of SVN
国信金太阳打新债步骤 开户是安全的吗
[proteus simulation] Arduino uno+ nixie tube display 4X4 keyboard matrix keys
Please check the list of commonly used software testing tools.
记一次beego通过go get命令后找不到bee.exe的坑
跨境电商新手如何防止店铺关联?用什么工具好?
AOSP ~ WIFI架构总览
Unity存档系统——Json格式的文件
PE file infrastructure sorting
Is it safe to open an account by fraud
使用XXL-JOB自定义任务并调度