当前位置:网站首页>Seata 1.3.0 four modes to solve distributed transactions (at, TCC, Saga, XA)
Seata 1.3.0 four modes to solve distributed transactions (at, TCC, Saga, XA)
2022-07-07 10:55:00 【Clang clang】
Preface
1、seata edition 1.3.0
2、 Infrastructure project structure , You just need to focus on Device modules device
and Work order module order
that will do .
-
project | explain |
---|---|
api-gateway | Gateway module |
common | Basic module |
device | Device modules |
order | Work order module |
user | User module |
3、 Database description , Device modules device
link gxm-301 database , Work order module order
link gxm-300 database
4、 Main business description , When generating work orders , We use order Service to gxm-300
Table of database work-order
and notice_info
Insert database , And call... Remotely device
Service insertion gxm-301 Table of database work-problem
And table work_order_problem_link
5、 Debugging instructions , We are using @GlobalTransactional
When annotating ,seata
The control transaction of is time limited, and the default is 1 minute , So in us debug
If the time is too long ,seata
Rollback by default , For the convenience of debugging , You can change this parameter .
6、 Official == Newcomer documentation It must be seen ==
One 、AT Pattern
1、 about seata The default is AT
Pattern , And if you rely on seata-spring-boot-starter when , Automatic proxy data source , There is no need for additional treatment
2、 about AT
The mode will be found when rolling back undo_log
Front and rear mirrors in , To recover .
But when recovering , It will compare whether the post image has been modified , That is to compare the data , If it's different , It indicates that the data has been modified by actions other than the current global transaction , This is the time
AT
When the mode performs data verification , Will fail to roll back , Because the verification fails , We can also turn off the verification process of the front and back images through configuration parameters , But this is not recommended , because , It was modified by other threads, which made it impossible to restore the scene , It really needs to be handled manually
3、 This is official AT
Instructions for using the mode , These are all points that must be paid attention to .
4、 One of the official Newcomer documentation It must be seen , In the old version , We want to proxy the data source , as follows , For specific mode, choose different data sources to proxy , For example, we are now simulating AT, That's it return new DataSourceProxy(druidDataSource);
.
@Primary
@Bean("dataSource")
public DataSource dataSource(DataSource druidDataSource) {
//AT agent A choice
return new DataSourceProxy(druidDataSource);
//XA agent
return new DataSourceProxyXA(druidDataSource)
}
5、 But how do you use a higher version or use seata-starter
, There is no need to manually configure , Because I use seata-starter
, and What I'm demonstrating now is AT
Pattern , So don't change anything ( Follow up XA
The mode will be modified )
6、 Common questions , The official has already given instructions and answers common problem , Before being used in production , It's best to read all these .
1.1、 Instructions
1.1.1、 Use
1、 We use annotations @GlobalTransactional
Turn on seata
Of AT
Pattern of transaction management , And because it uses seata-starter
, Then this annotation will automatically proxy AT
Data sources for patterns , The specific code is as follows , You can see that the code is mainly divided into two parts , The first part is to call yourself order
Service 2 Table mapper Insert data into gxm-300
, The second part is remote call device
Of the two mapper To insert data into gxm-301
@GlobalTransactional(name = "default", rollbackFor = Exception.class)
@Override
public R saveWithDetail(SaveWithDetailDTO saveWithDetailDTO) {
log.info("create order begin ... xid: " + RootContext.getXID());
String title = RandomUtil.randomString(20);
saveWithDetailDTO.setWorkOrderTitle(title);
saveWithDetailDTO.setWorkOrderNumber("asd");
// 1、 Call your own service
// 1.1、 Insert job information
WorkOrder workOrder = new WorkOrder();
BeanUtils.copyProperties(saveWithDetailDTO, workOrder);
this.baseMapper.insert(workOrder);
// 1.2、 Insert message notification table
NoticeInfo noticeInfo = new NoticeInfo();
noticeInfo.setTitle("new work order 【" + title + "】has publish");
noticeInfoMapper.insert(noticeInfo);
// When work order id Not for null when , Simulate an exception
if (saveWithDetailDTO.getId() != null) {
int i = 1 / 0;
}
// 2、 The remote invocation device service
// 2.1、 Insert the question table And problem association table
WorkProblemDTO workProblemDTO = new WorkProblemDTO();
BeanUtils.copyProperties(saveWithDetailDTO.getSoftwareNotSolveProblemList().get(0), workProblemDTO);
workProblemDTO.setOrderId(workOrder.getId());
workProblemApi.insertWithLink(workProblemDTO);
return R.ok();
}
@Override
public R insertWithLink(WorkProblemDTO workProblemDTO) {
WorkProblem workProblem = new WorkProblem();
BeanUtils.copyProperties(workProblemDTO, workProblem);
// 1、 Insert the question table
int insertProblem = workProblemMapper.insert(workProblem);
// 2、 Insert the work order problem association table
WorkOrderProblemLink workOrderProblemLink = new WorkOrderProblemLink();
workOrderProblemLink.setOrderId(workProblemDTO.getOrderId());
workOrderProblemLink.setProblemId(workProblem.getId());
int insertOrderProblemLink = workOrderProblemLinkMapper.insert(workOrderProblemLink);
if (insertProblem > 0 && insertOrderProblemLink > 0) {
return R.ok();
}
throw new RuntimeException(" Insertion exception ");
}
2、 Test the successful way first , That is, when transferring parameters id
It's empty , be 2 It's a database 4 There is no problem with all the tables , All inserted successfully , No problem
3、 The retest failed , That is, when transferring parameters id
Not empty , be seata Data global transactions will take effect ,2 A database 4 None of the tables has a database , explain seata
Of AT
The mode has taken effect ,
1.1.2、 Analysis
1、 Let's make a breakpoint , You can find AT
The secret of pattern , We directly put a breakpoint at the last position of the call chain , This position is 4 individual mapper Have been inserted successfully , however device
The service did not return , So the whole link is not over , And at this time, I lengthen the time of business , Enough for us to debug .
2、 When stopping at the end , We observe gxm-300
Database and gxm-301
database , You'll find that ,4 individual mapper The inserted data of has been inserted into the database , And one mapper Will be in the corresponding undo_log
Insert a piece of data into the table , There will be pre image data and post image data , as well as Branch id branch_id
3、gxm-300 Of undo_log
surface
id branch_id xid context rollback_info log_status log_created log_modified ext
7 278197152412237825 192.168.172.232:8091:278197152336740352 serializer=jackson (BLOB) 3.66 KB 0 2022-06-09 16:16:10 2022-06-09 16:16:10
8 278197152475152385 192.168.172.232:8091:278197152336740352 serializer=jackson (BLOB) 2.27 KB 0 2022-06-09 16:16:10 2022-06-09 16:16:10
4、 gxm-301 Of undo_log
surface
id branch_id xid context rollback_info log_status log_created log_modified ext
7 278197152550649857 192.168.172.232:8091:278197152336740352 serializer=jackson (BLOB) 982 bytes 0 2022-06-09 16:16:10 2022-06-09 16:16:10
8 278197152600981505 192.168.172.232:8091:278197152336740352 serializer=jackson (BLOB) 999 bytes 0 2022-06-09 16:16:10 2022-06-09 16:16:10
5、seata Of branch_table
surface
branch_id xid transaction_id resource_group_id resource_id branch_type status client_id application_data gmt_create gmt_modified
278197152412237825 192.168.172.232:8091:278197152336740352 278197152336740352 jdbc:mysql://127.0.0.1:3306/gxm-300 AT 0 OrderApplication-seata-id:192.168.172.232:56035 2022-06-09 16:16:09.791745 2022-06-09 16:16:09.791745
278197152475152385 192.168.172.232:8091:278197152336740352 278197152336740352 jdbc:mysql://127.0.0.1:3306/gxm-300 AT 0 OrderApplication-seata-id:192.168.172.232:56035 2022-06-09 16:16:09.806598 2022-06-09 16:16:09.806598
278197152550649857 192.168.172.232:8091:278197152336740352 278197152336740352 jdbc:mysql://127.0.0.1:3306/gxm-301 AT 0 DeviceApplication-seata-id:192.168.172.232:56409 2022-06-09 16:16:09.824506 2022-06-09 16:16:09.824506
278197152600981505 192.168.172.232:8091:278197152336740352 278197152336740352 jdbc:mysql://127.0.0.1:3306/gxm-301 AT 0 DeviceApplication-seata-id:192.168.172.232:56409 2022-06-09 16:16:09.837013 2022-06-09 16:16:09.837013
6、 seata Of global_table
surface
xid transaction_id status application_id transaction_service_group transaction_name timeout begin_time application_data gmt_create gmt_modified
192.168.172.232:8091:278197152336740352 278197152336740352 5 OrderApplication-seata-id my_test_tx_group default 600000 1654762569770 2022-06-09 16:16:09 2022-06-09 16:16:42
7、seata Of lock_table
surface
row_key xid transaction_id branch_id resource_id table_name pk gmt_create gmt_modified
jdbc:mysql://127.0.0.1:3306/gxm-300^^^notice_info^^^4 192.168.172.232:8091:278197152336740352 278197152336740352 278197152475152385 jdbc:mysql://127.0.0.1:3306/gxm-300 notice_info 4 2022-06-09 16:16:09 2022-06-09 16:16:09
jdbc:mysql://127.0.0.1:3306/gxm-300^^^work_order^^^4 192.168.172.232:8091:278197152336740352 278197152336740352 278197152412237825 jdbc:mysql://127.0.0.1:3306/gxm-300 work_order 4 2022-06-09 16:16:09 2022-06-09 16:16:09
jdbc:mysql://127.0.0.1:3306/gxm-301^^^work_order_problem_link^^^4 192.168.172.232:8091:278197152336740352 278197152336740352 278197152600981505 jdbc:mysql://127.0.0.1:3306/gxm-301 work_order_problem_link 4 2022-06-09 16:16:09 2022-06-09 16:16:09
jdbc:mysql://127.0.0.1:3306/gxm-301^^^work_problem^^^4 192.168.172.232:8091:278197152336740352 278197152336740352 278197152550649857 jdbc:mysql://127.0.0.1:3306/gxm-301 work_problem 4 2022-06-09 16:16:09 2022-06-09 16:16:09
8、 The relationship is branch_id
and transaction_id
, One transaction_id
Indicates the beginning of a global transaction , There will be more branch_id
Branch business
9、 If our business turns out to be ok ( It refers to the normal and successful insertion of business , Or there are exceptions but seata Of AT Mode helps you rollback , And there is no problem rolling back ), Then these tables will not have data , Because our overall affairs are over , Ensure the current business process , Even if it fails , But I rolled it back for you . Once our table has data , Just explain , Business execution is abnormal and seata There is a problem during rollback , This is the time ,seata The table data of relevant information will be stored , Don't delete , We see that we have to deal with it .
For example, in one case , We are in the exception process , Halfway through the first thread , namely
work_order
Table data insertion succeeded , But we modify it manually in the database , Or other thread transactions modify the newly produced data , But the first site implementation goes to the back , Ready to insertwork_problem
Something is wrong , So at this point ,seata Of at The mode will be rolled back according to the relevant logs , But when rolling back , It will check. , in the meantime `work_order`` The data just inserted , Has it been modified , Once it is inconsistent with its original record , Then it iu I can't help you deal with it . This is the time , The data of the related table is stored , We have to deal with it manually according to this information .
1.1.3、AT Mode rollback failed , Handle
1、 For the front 1.1.2 section
Debugging of , I have a problem here , Maybe it's because my breakpoint stays too long , You will find data in relevant tables , say seata Of at
Mode rollback failed . Next we have to deal with .
2、 See the global transaction id278197152336740352
3、 Find out which transaction branches are left under the current global transaction , As you can see, yes gxm-300
There is something wrong with our business , and pk
The fields are 4, The description is that the primary key of these two tables is 4 There's a problem with .
4、 So we find it in the corresponding data undo_log
, You can see the corresponding branch id It is also corresponding to the previous one , among rollback_info
The field records the data of the pre image and the data of the post image
5、 We click rollback_info
Field , Then save the data as xxx.json file , Open as follows
{
"@class":"io.seata.rm.datasource.undo.BranchUndoLog",
"xid":"192.168.172.232:8091:278197152336740352",
"branchId":278197152412237825,
"sqlUndoLogs":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.undo.SQLUndoLog",
"sqlType":"INSERT",
"tableName":"work_order",
"beforeImage":{
"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords",
"tableName":"work_order",
"rows":[
"java.util.ArrayList",
[
]
]
},
"afterImage":{
"@class":"io.seata.rm.datasource.sql.struct.TableRecords",
"tableName":"work_order",
"rows":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.sql.struct.Row",
"fields":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"id",
"keyType":"PRIMARY_KEY",
"type":4,
"value":4
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"work_order_number",
"keyType":"NULL",
"type":12,
"value":"asd"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"work_order_title",
"keyType":"NULL",
"type":12,
"value":"bfkzc4oganhbirygfd87"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"client_name",
"keyType":"NULL",
"type":12,
"value":" Yuan Yuhuan 2"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"client_contact",
"keyType":"NULL",
"type":12,
"value":" tencent 333"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"client_phone",
"keyType":"NULL",
"type":12,
"value":"181562383652"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"order_service_type",
"keyType":"NULL",
"type":4,
"value":1
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"order_type",
"keyType":"NULL",
"type":4,
"value":1
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"service_type",
"keyType":"NULL",
"type":4,
"value":3
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"deal_user_id",
"keyType":"NULL",
"type":4,
"value":20
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"order_content",
"keyType":"NULL",
"type":-1,
"value":" Weekend evening party 2"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"create_user_id",
"keyType":"NULL",
"type":4,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"create_time",
"keyType":"NULL",
"type":93,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"specify_processing_day",
"keyType":"NULL",
"type":91,
"value":[
"java.sql.Date",
1653321600000
]
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"receive_time",
"keyType":"NULL",
"type":93,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"receiver_submit_time",
"keyType":"NULL",
"type":93,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"end_time",
"keyType":"NULL",
"type":93,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"order_status",
"keyType":"NULL",
"type":4,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"receiver_refuse_content",
"keyType":"NULL",
"type":-1,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"receiver_deal_content",
"keyType":"NULL",
"type":-1,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"receiver_result_status",
"keyType":"NULL",
"type":4,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"send_refuse_content",
"keyType":"NULL",
"type":12,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"device_model",
"keyType":"NULL",
"type":12,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"device_number",
"keyType":"NULL",
"type":12,
"value":null
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"service_report_images",
"keyType":"NULL",
"type":-1,
"value":"http://gxm-tensquare.oss-cn-beijing.aliyuncs.com/2022-04/25/e214ba1b-6541-4756-8a2b-fdb1c29111e4.jpg"
}
]
]
}
]
]
}
}
]
]
}
6、 According to the above json The contents of the document , Direct operation is insert
, and device No insertion succeeded , But it inserted successfully , We can delete the relevant data , Later, if it is other situations , such as update
such , According to the front and back mirror data , Handle as required .
1.2、 Test the corresponding method without placing spring Transaction annotation for , Whether multiple businesses are rolled back normally
1、 For the above method , We know everything order
The service called the local 2 individual mapper, Insert into his linked database (gxm-300) in , And then remotely call deivce
Method of service , and device
Inside the method of is to call device
The local 2 individual mapper, Insert into his linked database (gxm-301) in , therefore , We don't add spring Transaction annotation for , I'm not here order
Service saveWithDetail
Methods and device
Service insertWithLink
Methodically used spring Transaction annotation for
2、 This result has been proved in our simple use in the first section , Yes. , We don't need to add the corresponding spring The transaction of ,seata It will be guaranteed .
3、 Of course someone might say , For the following method , Maybe device
Our business needs , Distributed transactions are not used , It takes spring To manage , I need to add spring Transaction annotations for , This statement , Of course you can add spring Transaction annotation for ,seata Does not affect the , But I don't recommend , Because under this category api It is provided for external calls , If it is internal, the business of this service should be handled in other classes , It shouldn't be here , It is inappropriate to be called internally .
1.3、 test seata When rolling back , After the mirrored data is modified by other transactions , Unable to rollback successful cases
1、 First of all, we need to understand seata Of AT
Mode flow , Official documents :Seata AT Pattern
2、 After reading this process , Everyone can make up a problem , It is as follows when the global transaction is not committed , The mirrored data has been modified by other sites , as follows , In this case seata There is no way to deal with , Unless you turn off the front and rear mirror check , Force data updates , This is too insecure .
Order service and inventory service .
After opening the global transaction , Inventory service has committed a local transaction ,50 The inventory is modified to 49, Global transaction not committed .
Another thread starts the local transaction , Modify inventory from 49 To 48.
However, the order service reported an error , The global transaction needs to be rolled back , At this time, the global transaction will fail to rollback , Dirty data .
Is there any good plan to deal with this situation ? Or avoid this situation
3、 This kind of problem ,github Of issue It has been put forward Dirty writes cause data rollback failure ?, This situation , There are two ways to deal with
4、 This official article also says very well Detailed explanation Seata AT Pattern transaction isolation level and global lock design , I also talked about the situation and treatment of dirty writing .
1.3.1、 Simulate this situation
1、 Or the previous interface , We modify seata Global control time , Because the test will take a long time
2、 We are device
The service is performing all mapper After pause 20 second , At this time, the database already has 2 It's a database 4 Table data , as follows
3、 Note that we set it up for a long time , So the remote call may timeout , Directly lead to seata Roll back , We have to call the modified thread urgently in the future , therefore , We need to modify the timeout of components called remotely , What I use here is dubbo, So I set up consumers order
Of dubbo The call timeout time of , It is amended as follows 30 second , enough . springBoot Integrate dubbo Time out settings for
# Set the timeout and retry times of remote calls
dubbo.provider.timeout=30000
dubbo.provider.retries=0
dubbo.consumer.timeout=30000
dubbo.consumer.retries=0
4、 Add a thread to modify uncommitted data , In front of the pause in 20 seconds , Let's call the following interface , Modify the data submitted for the transaction .
5、 Another thread we modified is device
Service data , therefore , You can see the console log as follows , and rm
At this time, I will keep trying , You can see the picture in the back , Try and fail , The console keeps printing messages about failed attempts .
6、 The data level is as follows , So the database gxm-300
and gxm-301
Of undo_log The total number of data pieces must be 3 strip .
1.3.2、 The first way to deal with it ( Handle by hand , Save according to business )
7、 We can use the above expression data , To manually process according to the business , Such as through lock_table Field of pk
, And current business know , Insert three more data of these three tables , The primary keys are 1 Of , So according to our business, we can delete relevant data directly . And remember seata The data of related tables in the database must also be deleted , And corresponding undo_log Table data also needs to be deleted .
1.3.3、 The second way to deal with it (@GlobalLock)
1、 Add to the locally modified transaction @GlobalLock
- The parameter
lockRetryTimes
Try interval ,lockRetryTimes
Number of attempts , Explain how many times in seconds the global lock will be retried repeatedly , If the record is in the global transaction , It will fail- These two parameters are in 1.4.0 And the version above ,1.3.0m Not yet .
2、 You can refer to Seata Introductory series (22)[email protected] Annotation usage scenarios and source code analysis , That's good.
3、 Another modified thread finds the modified data in the global transaction , Therefore, modification is not supported .
4、 Transaction rollback succeeded ,undo_log The front image data of is consistent with the data of the database , Note that it has not been modified by the previous thread .
Two 、TCC Pattern
1、 Actually TCC
Patterns and AT
The process is the same , It's just AT
Is automatically based on undo_log
Transaction rollback and compensation , and TCC
We need to provide corresponding interfaces , Officials have also indicated Seata TCC Pattern , You can see TCC The first and second phases of are custom logic ,seata Just call . and AT
It all depends on undo_log
, then seata Judge to help you deal with .
2、 Here we need to introduce some basic annotations and parameters that need to be used later
@LocalTCC
Apply to SpringCloud+Feign Mode of TCC, But when I experimented , The call uses dubbo, Theoretically, this annotation is not needed ( Official demo of use dubbo Of did not add this annotation ), But I tried , If you don't add it, it will appear tcc BusinessActionContext get null , The official has not dealt with it yet , I wonder what the problem is , I also reply below .@TwoPhaseBusinessAction
annotation try Method , among name For the current tcc Methodical bean name , Write the method name ( Remember that the whole situation is unique ),commitMethod Point to the submit method ,rollbackMethod Point to the transaction rollback method . After specifying three methods ,seata Depending on the success or failure of the global transaction , To help us automatically call the commit method or rollback method .@BusinessActionContextParameter
Annotations can pass parameters to phase two (commitMethod/rollbackMethod) Methods , This is also the problem mentioned below , The parameters obtained in the second stage can only be the parameter values defined by annotations at the beginning of the first stage , Even if you modify it in the first stage , add to , It is also impossible to obtain the latest parameter value in the second stage .BusinessActionContext Refers to TCC Transaction context , You can get... Through this parameter
xid
、branchId
、actionName
, And some parameters , Be careful , The problem here is On prepare Stage , That is to say try Add parameters to the data of the stage code , Or modify the parameters , stay confrim and cancel The modified data cannot be accepted in the phase method .
3、TCC Participants need to implement three methods , They are the first stage Try Method 、 Two stages Confirm Method and two stages Cancel Method . stay TCC Participants need to add @TwoPhaseBusinessAction annotation , And declare these three methods , As shown below
public interface TccAction {
@TwoPhaseBusinessAction(name = "yourTccActionName", commitMethod = "confirm", rollbackMethod = "cancel")
public boolean try(
BusinessActionContext businessActionContext, int a, int b);
public boolean confirm(BusinessActionContext businessActionContext);
public boolean cancel(BusinessActionContext businessActionContext);
}
@TwoPhaseBusinessAction
Annotation property description :
name
:TCC Name of participant , Customizable , But it must be globally unique .commitMethod
: Specify two stages Confirm Method name , Customizable .rollbackMethod
: Specify two stages Cancel Method name , Customizable .
4、TCC Method parameter description :
Try
: The first parameter type must beBusinessActionContext
, The number and type of subsequent parameters can be customized .Confirm
: There is only one parameter , Parameter type must be BusinessActionContext, The following is the corresponding parameter name (businessActionContext
).Cancel: There is only one parameter , Parameter type must be
BusinessActionContext
, The following is the corresponding parameter name (businessActionContext
).
5、TCC Method return type description :
A stage of
Try
The method can be boolean type , You can also customize the return value .Two stage
Confirm
and Cancel The return type of the method must be boolean type .
6、 The function of each interface :( Below demo In fact, it is not strictly implemented in this way , It is recommended that the production environment follow the following steps to ensure , Create a resource reservation table to lock resources , You can refer to this article , Native TCC Realization )
You can refer to demo, Native TCC Realization https://github.com/prontera/spring-cloud-rest-tcc/tree/readme-img, There is a resource table , be used for try Stage , Reserve resources .
Try
: Preliminary operation . Complete all business checks , Reserve necessary business resources .( such as select for update Lock a record , Reserve specified resources )Confirm
: Confirm operation . Real business logic ( For example, according totry
The data of , Operations such as updating inventory ), No business checks , Use only Try Business resources reserved in the stage . therefore , as long asTry
Successful operation ,Confirm
It's bound to succeed . in addition ,Confirm The operation needs to satisfy idempotence , Ensure that a distributed transaction can and can only succeed once .Cancel
: Cancel operation . ReleaseTry
Business resources reserved in the stage . alike ,Cancel
Operations also need to satisfy idempotence
2.1、 Code emulation
2.1.1、 Business service
1、 Or the basic project above , But it needs a little change , Let's take a sample that specializes in dealing with complex businesses service
Class out , Call... Respectively order
Service and device
service , Look clear in this way , as follows , At call time ,BusinessActionContext
Parameters , Let's pass it on null that will do ,seata It will be assigned a value .
@GlobalTransactional(name = "default", rollbackFor = Exception.class, timeoutMills = 60000 * 10)
@Override
public R saveWithDetail(SaveWithDetailDTO saveWithDetailDTO) {
log.info("create order begin ... xid: " + RootContext.getXID());
// 1、order service
workOrderService.simpleSave(null, saveWithDetailDTO);
// 2、 The remote invocation device service
// 2.1、 Insert the question table And problem association table
WorkProblemDTO workProblemDTO = new WorkProblemDTO();
BeanUtils.copyProperties(saveWithDetailDTO.getSoftwareNotSolveProblemList().get(0), workProblemDTO);
workProblemDTO.setOrderId(saveWithDetailDTO.getId());
workProblemApi.insertWithLink(null, workProblemDTO);
return R.ok();
}
2、 Abnormal simulation we put device
In service
2.1.2、order service
1、 Note that we need to annotate the interface @LocalTCC
, Turn on tcc Business , And add notes on the method of the first stage @TwoPhaseBusinessAction
, And assign the value of the annotation , Indicates the second stage commit
and rollback
What are the methods , as well as The return value of the three methods is boolean
@LocalTCC
public interface WorkOrderService extends IService<WorkOrder> {
String simpleSave_BusinessActionContextParameter = "saveWithDetailDTO";
/** * Add work order * * @param saveWithDetailDTO saveWithDetailDTO */
@TwoPhaseBusinessAction(name = "DubboTccSimpleSaveActionOne", commitMethod = "simpleSaveCommit", rollbackMethod = "simpleSaveRollback")
boolean simpleSave(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = simpleSave_BusinessActionContextParameter) SaveWithDetailDTO saveWithDetailDTO);
/** * Commit boolean. * This method needs to maintain idempotence and anti suspension * * @param actionContext the action context * @return the boolean */
public boolean simpleSaveCommit(BusinessActionContext actionContext);
/** * Rollback boolean. * This method needs to maintain idempotence and anti suspension * * @param actionContext the action context * @return the boolean */
public boolean simpleSaveRollback(BusinessActionContext actionContext);
}
2、 Implementation class , Because the business I simulated is inserting , When rolling back in the second stage , Compensation is definitely more new id Delete it , But I tried , In the first stage actionContext#map
Add parameters , Or modify saveWithDetailDTO
Parameters , Neither. , In the second stage, only the initial parameters can be obtained saveWithDetailDTO
value , That's what I mentioned earlier , If you have the same business needs as me , Consider putting it in redis Take it inside id, wait .
@Slf4j
@Service
public class WorkOrderServiceImpl implements WorkOrderService {
@DubboReference
private WorkDeviceApi workDeviceApi;
@DubboReference
private WorkProblemApi workProblemApi;
@Autowired
private NoticeInfoMapper noticeInfoMapper;
private static final String INSERT_ORDER_ID_KEY = "INSERT_ORDER_ID_KEY";
private static final String INSERT_NOTICE_INFO_ID_KEY = "INSERT_NOTICE_INFO_ID_KEY";
/** * * @param saveWithDetailDTO saveWithDetailDTO * @return */
// @Transactional Of course, this method can also be added spring Of Transactional annotation
@Override
public boolean simpleSave(BusinessActionContext actionContext, SaveWithDetailDTO saveWithDetailDTO) {
// attribute BusinessActionContext We don't need to inject ,seata It will inject into us
String actionName = actionContext.getActionName();
String xid = actionContext.getXid();
long branchId = actionContext.getBranchId();
String title = RandomUtil.randomString(20);
saveWithDetailDTO.setWorkOrderTitle(title);
saveWithDetailDTO.setWorkOrderNumber("asd");
// 1、 Call your own service
// 1.1、 Insert job information
WorkOrder workOrder = new WorkOrder();
BeanUtils.copyProperties(saveWithDetailDTO, workOrder);
this.baseMapper.insert(workOrder);
saveWithDetailDTO.setId(workOrder.getId());
// Even if you make changes in this theory BusinessActionContext Stored data , But you are in the second stage (commit/rollback) You can't get the modified data
// You can only get the data initialized at the beginning , Ken is in the second stage BusinessActionContext object , It's a new instance , Only the initial data
// There is no data to be modified later
// Map<String, Object> actionContextMap = actionContext.getActionContext();
// actionContextMap.put(INSERT_ORDER_ID_KEY, workOrder.getId());
// 1.2、 Insert message notification table
NoticeInfo noticeInfo = new NoticeInfo();
noticeInfo.setTitle("new work order 【" + title + "】has publish");
noticeInfoMapper.insert(noticeInfo);
// actionContextMap.put(INSERT_NOTICE_INFO_ID_KEY, noticeInfo.getId());
return true;
}
@Override
public boolean simpleSaveCommit(BusinessActionContext actionContext) {
// Here you can get At the beginning prepare Annotate the stage BusinessActionContextParameter Value
log.info("simpleSave Commit, params : {}", JSONUtil.toJsonStr(actionContext.getActionContext(simpleSave_BusinessActionContextParameter)));
//todo If resources are reserved in the first stage , Here you have to submit resources
// Indicates whether it was successful
return true;
}
@Override
public boolean simpleSaveRollback(BusinessActionContext actionContext) {
// Here you can get At the beginning prepare Annotate the stage BusinessActionContextParameter Value
JSONObject saveWithDetailDTOJSONObject = (JSONObject) actionContext.getActionContext(simpleSave_BusinessActionContextParameter);
log.info("simpleSave Commit , params : {}", JSONUtil.toJsonStr(saveWithDetailDTOJSONObject));
// Compensation measures , as follows
// 1、 Solve idempotency Work order form id It's empty , It indicates that the first step has not been successfully implemented , There is no need to compensate for this step
// Here can be changed from redis In order to get , such Integer orderId = (Integer) actionContext.getActionContext(INSERT_ORDER_ID_KEY);, It is impossible to obtain the value stored at the end of the first section
// I write it here for the convenience of demonstration 1 了 , Because after every demonstration , Metropolis truncate table
Integer orderId = 1;
if (orderId == null) {
return true;
} else {
// Delete insert work_order The data table
this.baseMapper.deleteById(orderId);
}
// 2、 Solve idempotency Message notification table id It's empty , Description not inserted , There is no need to compensate for this step
// Here can be changed from redis In order to get , such Integer noticeInfoId = (Integer) actionContext.getActionContext(INSERT_NOTICE_INFO_ID_KEY);, It is impossible to obtain the value stored at the end of the first section
// I write it here for the convenience of demonstration 1 了 , Because after every demonstration , Metropolis truncate table
Integer noticeInfoId = 1;
if (noticeInfoId == null) {
return true;
} else {
// Delete insert notice_Info The data table
noticeInfoMapper.deleteById(noticeInfoId);
}
return true;
}
}
2.1.3、device service
1、 Note that we need to annotate the interface @LocalTCC
, Turn on tcc Business , And add notes on the method of the first stage @TwoPhaseBusinessAction
, And assign the value of the annotation , Indicates the second stage commit
and rollback
What are the methods , as well as The return value of the three methods is boolean
@LocalTCC
public interface WorkProblemApi {
String insertWithLink_BusinessActionContextParameter = "workProblemDTO";
/** * Insertion time , Insert the corresponding work order question table * * @param workProblemDTO * @return */
@TwoPhaseBusinessAction(name = "DubboTccInsertWithLinkActionTwo", commitMethod = "insertWithLinkCommit", rollbackMethod = "insertWithLinkRollback")
boolean insertWithLink(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = insertWithLink_BusinessActionContextParameter) WorkProblemDTO workProblemDTO);
/** * Commit boolean. * * @param actionContext the action context * @return the boolean */
public boolean insertWithLinkCommit(BusinessActionContext actionContext);
/** * Rollback boolean. * * @param actionContext the action context * @return the boolean */
public boolean insertWithLinkRollback(BusinessActionContext actionContext);
}
2、 Implementation class , Because the business I simulated is inserting , When rolling back in the second stage , Compensation is definitely more new id Delete it , But I tried , In the first stage actionContext#map
Add parameters , Or modify workProblemDTO
Parameters , Neither. , In the second stage, only the initial parameters can be obtained workProblemDTO
value , That's what I mentioned earlier , If you have the same business needs as me , Consider putting it in redis Take it inside id, wait .
@Slf4j
@DubboService
public class WorkProblemApiImpl implements WorkProblemApi {
@Autowired
private WorkProblemMapper workProblemMapper;
@Autowired
private WorkOrderProblemLinkMapper workOrderProblemLinkMapper;
private static final String INSERT_PROBLEM_ID_KEY = "INSERT_PROBLEM_ID_KEY";
private static final String INSERT_ORDER_PROBLEM_LINK_ID_KEY = "INSERT_ORDER_PROBLEM_LINK_ID_KEY";
@Override
public boolean insertWithLink(BusinessActionContext actionContext, WorkProblemDTO workProblemDTO) {
WorkProblem workProblem = new WorkProblem();
BeanUtils.copyProperties(workProblemDTO, workProblem);
// 1、 Insert the question table
int insertProblem = workProblemMapper.insert(workProblem);
// 2、 Insert the work order problem association table
WorkOrderProblemLink workOrderProblemLink = new WorkOrderProblemLink();
workOrderProblemLink.setOrderId(workProblemDTO.getOrderId());
workOrderProblemLink.setProblemId(workProblem.getId());
int insertOrderProblemLink = workOrderProblemLinkMapper.insert(workOrderProblemLink);
// Simulate anomalies
int i = 1 / 0;
if (insertProblem > 0 && insertOrderProblemLink > 0) {
return true;
}
throw new RuntimeException(" Insertion exception ");
}
@Override
public boolean insertWithLinkCommit(BusinessActionContext actionContext) {
// Here you can get At the beginning prepare Annotate the stage BusinessActionContextParameter Value
log.info("insertWithLink commit, params : {}", JSONUtil.toJsonStr(actionContext.getActionContext(insertWithLink_BusinessActionContextParameter)));
//todo If resources are reserved in the first stage , Here you have to submit resources
// Indicates whether it was successful
return true;
}
@Override
public boolean insertWithLinkRollback(BusinessActionContext actionContext) {
// Here you can get At the beginning prepare Annotate the stage BusinessActionContextParameter Value
JSONObject workProblemDTOJSONObject = (JSONObject) actionContext.getActionContext(insertWithLink_BusinessActionContextParameter);
log.info("insertWithLink Rollback, params : {}", JSONUtil.toJsonStr(workProblemDTOJSONObject));
// Compensation measures , as follows
// 1、 Solve idempotency List of questions id It's empty , It indicates that the first step has not been successfully implemented , There is no need to compensate for this step
// Here can be changed from redis In order to get , such (Integer) actionContext.getActionContext(INSERT_PROBLEM_ID_KEY);, It is impossible to obtain the value stored at the end of the first section
// I write it here for the convenience of demonstration 1 了 , Because after every demonstration , Metropolis truncate table
Integer insertProblemId = 1;
if (insertProblemId == null) {
return true;
} else {
// Delete insert work_order The data table
this.workProblemMapper.deleteById(insertProblemId);
}
// 2、 Solve idempotency Work order problem association table id It's empty , Description not inserted , There is no need to compensate for this step
// Here can be changed from redis In order to get , such (Integer) actionContext.getActionContext(INSERT_ORDER_PROBLEM_LINK_ID_KEY); It is impossible to obtain the value stored at the end of the first section
// I write it here for the convenience of demonstration 1 了 , Because after every demonstration , Metropolis truncate table
Integer insertOrderProblemLinkId = 1;
if (insertOrderProblemLinkId == null) {
return true;
} else {
// Delete insert work_order_problem_link The data table
workOrderProblemLinkMapper.deleteById(insertOrderProblemLinkId);
}
return true;
}
}
2.1.4、 Test and analysis results
1、 We are 4 individual mapper When it's all finished , And make a breakpoint where the exception has not yet occurred , as follows
2、4 All the business tables are inserted For your data , At this time, because we use tcc
Pattern ,rollback
Things need to be handled by ourselves , therefore undo_log
There is no data in the table , You can also delete this directly undo_log surface
3、seata Server side 3 A watch , You can see branch_table
The branch type in the table has been changed to TCC
Pattern , One, two branches , Namely order
Service tcc, and device
Service tcc
, There is another field in the table application-data
Is the data you operate .
4、 After releasing the breakpoint , You can see that an exception has occurred , therefore 2 A service (4 individual mapper) We have to roll back
5、order
The service log analysis is as follows
6、device
The service log analysis is as follows
7、 view the database , Of course, you can also see the self increment of each table id, Whether it has been from 2 Here we go , If from 2 Here we go , Just explain , There was an insertion before , However, it was later rolled back and deleted .
2.2、 How to control exceptions
1、 This part comes from seata-TCC Pattern
2、 stay TCC During the execution of the model , There may also be various exceptions , One of the most common is idle rollback 、 idempotent 、 Hanging, etc . Now let me talk about Seata How to handle these three exceptions
2.2.1、 How to handle empty rollback
1、 What is an empty rollback ?
Empty rollback refers to a distributed transaction , When there is no calling party Try In the case of method ,TM Driving the two-phase rollback calls the participant's Cancel Method .
2、 So how does empty rollback come about ?
After the global transaction is started , participants A After the branch registration is completed, the participant phase will be executed RPC Method , If participants at this time A The machine is down , Network anomalies , Will cause RPC Call failed , That is, participants A The one-stage method was not successfully implemented , But at this time, the global transaction has been started ,Seata It must be advanced to the final state , Participants will be called when the global transaction is rolled back A Of Cancel Method , This causes an empty rollback .
3、 To prevent empty rollback , Then it must be in Cancel Method to identify that this is an empty rollback ,Seata How is it done ?
Seata The way to do this is to add a TCC Transaction control table , Containing transactions XID and BranchID Information , stay Try Insert a record when the method executes , Indicates that a stage has been implemented , perform Cancel Method to read this record , If the record does not exist , explain Try Method not implemented .
2.2.2、 How to deal with idempotent
1、 Idempotent problem refers to TC Repeat the two-stage submission , therefore Confirm/Cancel The interface needs to support idempotent processing , That is, resources will not be repeatedly committed or released .
2、 So how does idempotent problem come into being ?
In the participants A After the second stage , Due to network jitter or downtime , Can cause TC No participants received A Execute the return result of the second stage ,TC The call will be repeated , Until the result of the second stage implementation is successful .
3、Seata How to deal with idempotent problem ?
The same is true in TCC Add a field of record status in the transaction control table status, This field has 3 It's worth , Respectively :
- tried:1
- committed:2
- rollbacked:3
Two stages Confirm/Cancel After method execution , Change the state to committed or rollbacked state . When calling the second stage repeatedly Confirm/Cancel When the method is used , The idempotent problem can be solved by judging the transaction state .
2.2.3、 How to deal with suspension
1、 Suspension refers to the second stage Cancel Method ratio A stage Try The method takes precedence , Because empty rollback is allowed , At the end of the second stage Cancel Method, and then the direct null rollback returns success , At this time, the global transaction has ended , But because of Try Method is then executed , This will create a stage Try The resources reserved by method can never be committed and released .
2、 So how did the suspension come into being ?
In execution participants A A phase of Try When the method is used , Network congestion , because Seata There is a timeout limit for global transactions , perform Try After method timeout ,TM Decide to rollback globally , After the rollback is completed, if at this time RPC The request arrived at the participant A, perform Try Method to reserve resources , This causes suspension .
3、Seata How to deal with suspension ?
stay TCC Fields of transaction control table record status status Add a status in :
- suspended:4
When performing phase II Cancel When the method is used , If you find that TCC There are relevant records in the transaction control table , Explain the second stage Cancel Method first stage Try Method execution , So insert a status=4 A record of the state , When a stage Try When the method is executed later , Judge status=4 , Then there are two stages Cancel Has been carried out , And back to false To stop a stage Try Method executed successfully .
4、 Parameters can be added to the code useTCCFence = true
, Turn on seata Put the hanging
@TwoPhaseBusinessAction(name = "beanName", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
3、 ... and 、SAGA Pattern
1、 This saga
There are so many pattern pits , Mainly, the official documents are too messy , But in fact, the test cases under the source code are still good , There are too few documents , In order to find a situation that can be used in production , I'm really nosing around .
2、 The first point , Official saga
The documentation of the pattern is Be sure to see ,SEATA Saga Pattern , After reading it, you can get a general understanding , We all need to know the meaning of the parameters of the state language , Otherwise, it can't be written later .
3、 Then the official code example , It is suggested that the young partner who just started , Be sure to go through it first , I have a bottom
- Test cases under the source code ,
io.seata.saga.engine.StateMachineTests
, List almost all the state machine situations
- There is also an official example code , The project address is seata-samples, Find an example of the project situation you need , Current
saga
The pattern is as follows
3.1、 Code emulation
1、 According to the above official documents and sample project code , We know ,saga
Currently, a state machine based approach is provided , The official language of state machine also provides a visual interface State machine designer presentation address :http://seata.io/saga_designer/index.html
But this online tool , It seems that some old versions are not supported
This is not as detailed as above , It should be the first version
3.1.1 Create database
1、saga
The schema needs to add some tables to the database of the service initiator , Of course saga
There are memory based databases (H2) The pattern of , But the authorities don't recommend that you do that .
2、 As follows
3、 perform sql Script , Because my subsequent demonstration is from order
Service initiation , The database used is gxm-300
, So I have this sql Where the script is executed , As shown in the figure below, three tables are added .
3.1.2 Business code
1、 It's the same as before , Let's take a sample that specializes in dealing with complex businesses service
Class out , Call and... Respectively device
service
order
service ( Use gxm-300 Databasework_order
Table andnotice_info
surface )device
service ( Use gxm-301 Databasework_order_problem_link
Table andwork_problem
surface )
3.1.2.1 order service
1、WorkOrderService Interface class , One is the business method , The other is the compensation method for business failure .
2、WorkOrderServiceImpl Implementation class , One is the business method , The other is the compensation method for business failure .
package cn.gxm.order.service.impl;
import cn.gxm.order.dto.method.service.savewithdetail.SaveWithDetailDTO;
import cn.gxm.order.mapper.NoticeInfoMapper;
import cn.gxm.order.mapper.WorkOrderMapper;
import cn.gxm.order.pojo.NoticeInfo;
import cn.gxm.order.pojo.WorkOrder;
import cn.gxm.order.service.WorkOrderService;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/** * @author GXM * @version 1.0.0 * @Description TODO * @createTime 2022 year 04 month 14 Japan */
@Slf4j
@Service
public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private NoticeInfoMapper noticeInfoMapper;
/** * test seata * * @param saveWithDetailDTO saveWithDetailDTO * @return */
@Override
public SaveWithDetailDTO simpleSave(String businessKey, SaveWithDetailDTO saveWithDetailDTO) {
// You can see our workProblemApi Is here or not spring Inside
// System.out.println(applicationContext.getBeanDefinitionCount());
// for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
// System.out.println(beanDefinitionName);
// }
String title = RandomUtil.randomString(20);
saveWithDetailDTO.setWorkOrderTitle(title);
saveWithDetailDTO.setWorkOrderNumber("asd");
// 1、 Call your own service
// 1.1、 Insert job information
WorkOrder workOrder = new WorkOrder();
BeanUtils.copyProperties(saveWithDetailDTO, workOrder);
workOrder.setBusinessKey(businessKey);
this.baseMapper.insert(workOrder);
saveWithDetailDTO.setId(workOrder.getId());
// 1.2、 Insert message notification table
NoticeInfo noticeInfo = new NoticeInfo();
noticeInfo.setTitle("new work order 【" + title + "】has publish");
noticeInfo.setBusinessKey(businessKey);
noticeInfoMapper.insert(noticeInfo);
saveWithDetailDTO.setNoticeInfoId(noticeInfo.getId());
return saveWithDetailDTO;
}
@Override
public boolean compensateCreateOrder(String businessKey) {
log.info("compensateCreateOrder business key : {}", businessKey);
if (StrUtil.isNotBlank(businessKey)) {
// 1、 according to business key To operate compensate Because my business here is to insert , therefore , I directly based on business key Delete relevant data
// 1.1、 Delete work_order Table data
LambdaQueryWrapper<WorkOrder> workOrderQueryWrapper = new LambdaQueryWrapper<>();
workOrderQueryWrapper.eq(WorkOrder::getBusinessKey, businessKey);
this.baseMapper.delete(workOrderQueryWrapper);
// 1.2、 Delete notice_info Table data
LambdaQueryWrapper<NoticeInfo> noticeInfoQueryWrapper = new LambdaQueryWrapper<>();
noticeInfoQueryWrapper.eq(NoticeInfo::getBusinessKey, businessKey);
noticeInfoMapper.delete(noticeInfoQueryWrapper);
}
return true;
}
}
3.1.2.1 device service
1、WorkProblemApi Interface class , One is the business method , The other is the compensation method for business failure .
2、WorkProblemApiImpl Implementation class , One is the business method , The other is the compensation method for business failure .
package cn.gxm.device.client;
import cn.gxm.device.api.WorkProblemApi;
import cn.gxm.device.dto.common.WorkProblemDTO;
import cn.gxm.device.mapper.WorkOrderProblemLinkMapper;
import cn.gxm.device.mapper.WorkProblemMapper;
import cn.gxm.device.pojo.WorkOrderProblemLink;
import cn.gxm.device.pojo.WorkProblem;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
/** * @author GXM * @version 1.0.0 * @Description TODO * @createTime 2022 year 05 month 19 Japan */
@Slf4j
@DubboService
public class WorkProblemApiImpl implements WorkProblemApi {
@Autowired
private WorkProblemMapper workProblemMapper;
@Autowired
private WorkOrderProblemLinkMapper workOrderProblemLinkMapper;
@Override
public WorkProblemDTO insertWithLink(String businessKey, WorkProblemDTO workProblemDTO) {
WorkProblem workProblem = new WorkProblem();
BeanUtils.copyProperties(workProblemDTO, workProblem);
workProblem.setBusinessKey(businessKey);
// 1、 Insert the question table
workProblemMapper.insert(workProblem);
workProblemDTO.setId(workProblem.getId());
// 2、 Insert the work order problem association table
WorkOrderProblemLink workOrderProblemLink = new WorkOrderProblemLink();
workOrderProblemLink.setOrderId(workProblemDTO.getOrderId());
workOrderProblemLink.setProblemId(workProblem.getId());
workOrderProblemLink.setBusinessKey(businessKey);
workOrderProblemLinkMapper.insert(workOrderProblemLink);
workProblemDTO.setOrderProblemLinkId(workOrderProblemLink.getId());
if (workProblemDTO.getProblem().equals("exception")) {
int i = 1 / 0;
}
return workProblemDTO;
}
/** * Business compensation * * @param businessKey seata Business key * @return */
@Override
public boolean compensateInsertWithLink(String businessKey) {
log.info("compensateInsertWithLink business key : {}", businessKey);
if (StrUtil.isNotBlank(businessKey)) {
// 1、 according to business key To operate compensate Because my business here is to insert , therefore , I directly based on business key Delete relevant data
// 1.1、 Delete work_problem Table data
LambdaQueryWrapper<WorkProblem> workProblemQueryWrapper = new LambdaQueryWrapper<>();
workProblemQueryWrapper.eq(WorkProblem::getBusinessKey, businessKey);
workProblemMapper.delete(workProblemQueryWrapper);
// 1.2、 Delete notice_info Table data
LambdaQueryWrapper<WorkOrderProblemLink> workOrderProblemLinkQueryWrapper = new LambdaQueryWrapper<>();
workOrderProblemLinkQueryWrapper.eq(WorkOrderProblemLink::getBusinessKey, businessKey);
workOrderProblemLinkMapper.delete(workOrderProblemLinkQueryWrapper);
}
return true;
}
}
3.1.2.3 Comprehensive complex business
1、 I wrote this type directly order
Under service
2、 Implementation class
package cn.gxm.order.service.impl;
import cn.gxm.common.resp.R;
import cn.gxm.order.dto.method.service.savewithdetail.SaveWithDetailDTO;
import cn.gxm.order.service.BusinessService;
import io.seata.core.context.RootContext;
import io.seata.saga.engine.StateMachineEngine;
import io.seata.saga.statelang.domain.StateMachineInstance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/** * @author GXM * @version 1.0.0 * @Description TODO * @createTime 2022 year 06 month 10 Japan */
@Service
@Slf4j
public class BusinessServiceImpl implements BusinessService {
@Autowired
private StateMachineEngine stateMachineEngine;
/** * The business logic is * // 1、order service * workOrderService.simpleSave(saveWithDetailDTO); * // 2、 The remote invocation device service * // 2.1、 Insert the question table And problem association table * WorkProblemDTO workProblemDTO = new WorkProblemDTO(); * BeanUtils.copyProperties(saveWithDetailDTO.getSoftwareNotSolveProblemList().get(0), workProblemDTO); * workProblemDTO.setOrderId(saveWithDetailDTO.getId()); * workProblemApi.insertWithLink(workProblemDTO); * * @param saveWithDetailDTO * @return */
@Override
public R saveWithDetailInStatemachineEngine(SaveWithDetailDTO saveWithDetailDTO) {
log.info("create order begin ... xid: " + RootContext.getXID());
String businessKey = String.valueOf(System.currentTimeMillis());
// 1、 The following state machine describes the above process
Map<String, Object> paramMap = new HashMap<>(1);
// 1.1、 This `saveWithDetailDTOKey`, It can be in the state machine json Through the file $.[saveWithDetailDTOKey] obtain , And add `.` Can represent specific data
paramMap.put("saveWithDetailDTOKey", saveWithDetailDTO);
paramMap.put("businessKey", businessKey);
String stateMachineName = "createOrderAndProblemStateMachine";
// 1.2、 Execute state machine json file , The specific business processes are json In file .
// StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
StateMachineInstance instance = stateMachineEngine.startWithBusinessKey(stateMachineName, null, businessKey, paramMap);
log.info(" The most general implementation result : {}; xid : {}; businessKey: {}; compensationStatus {}",
instance.getStatus(), instance.getId(), instance.getBusinessKey(), instance.getCompensationStatus());
return R.ok();
}
}
3.1.3 Project configuration saga Pattern
1、 First, we need to write a state language file of our business , To show your business situation , And rollback compensation , You can use the official online tool mentioned above
I won't elaborate on some grammar , Pull the official document to the bottom , That is to say, these semantic , If you don't understand, you can go and have a look
{
"Name": "createOrderAndProblemStateMachine", # The name of the state machine , Follow up use should be based on this unique to find
"Comment": " Create a work order state machine ", # brief introduction
"StartState": "CreateOrder", # The initial state
"Version": "0.0.1", # current version
"States": {
# Status list
"CreateOrder": {
# be known as CreateOrder Status list of
"Type": "ServiceTask", # type
"ServiceName": "workOrderServiceImpl", # Corresponding services bean name ,saga Will arrive spring Of bean Find this name in the container bean.
"ServiceMethod": "simpleSave", # workOrderServiceImpl The name is simpleSave Methods
"Next": "ChoiceState", # Next state
"CompensateState": "CompensateCreateOrder", # The name of the compensation status of the current service ( There are definitions below )
"ParameterTypes": [ # workOrderServiceImpl#simpleSave Method parameter type ( Don't write , But if there are generics , Just write , Official note )
"java.lang.String",
"cn.gxm.order.dto.method.service.savewithdetail.SaveWithDetailDTO"
],
"Input": [ # workOrderServiceImpl#simpleSave Parameter value of the method of
"$.[businessKey]",
"$.[saveWithDetailDTOKey]"
],
"Output": {
# workOrderServiceImpl#simpleSave The return value of the method , Stored in the context of the state machine ,key yes simpleSaveResult, Value is the entire return result of the method
"simpleSaveResult": "$.#root"
},
"Status": {
# At present CreateOrder The state of Service execution state mapping , The framework defines three states ,SU success 、FA Failure 、UN Unknown , We need to map the state of service execution to these three states , Help the framework judge the consistency of the whole transaction , It's a map structure ,key Is a conditional expression , Generally, it takes the return value of the service or the exception thrown for judgment , The default is SpringEL The expression determines the service return parameter , belt $Exception{
The beginning indicates to judge the type of exception .value When the conditional expression is true, the service execution state is mapped to this value
# One thing to say here is that the abnormal judgment should be put in front , Otherwise, if you put the judgment based on the return value in front , Once something goes wrong , Then the method has no return value , So this one #root.id Is the wrong grammar , because #root yes null, Of course, the most general state is "UN"
"$Exception{java.lang.Throwable}": "UN",
"#root.id != null && #root.noticeInfoId != null": "SU",
"#root.id == null || #root.noticeInfoId == null": "FA"
}
},
"ChoiceState": {
"Type": "Choice",
"Choices": [
{
# Only CreateOrder Phase creation succeeded (work_order Yes id 了 , also notice_info Also have id, Note the insertion was successful ), Before taking the next step
"Expression": "[simpleSaveResult].id != null && [simpleSaveResult].noticeInfoId != null",
"Next": "CreateProblem"
}
],
"Default": "Fail" # Otherwise, default to failure ( Failure status is defined below )
},
"CreateProblem": {
"Type": "ServiceTask",
"ServiceName": "workProblemApi",
"ServiceMethod": "insertWithLink",
"CompensateState": "CompensateCreateProblem",
"Input": [
"$.[businessKey]",
{
"problem": "$.[saveWithDetailDTOKey].softwareNotSolveProblemList[0].problem",
"type": "$.[saveWithDetailDTOKey].softwareNotSolveProblemList[0].type",
"orderId": "$.[simpleSaveResult].id"
}
],
"Output": {
"insertWithLinkResult": "$.#root"
},
"Status": {
"$Exception{java.lang.Throwable}": "UN",
"#root.id != null && #root.orderProblemLinkId != null": "SU",
"#root.id == null || #root.orderProblemLinkId == null": "FA"
},
"Catch": [
{
"Exceptions": [
"java.lang.Throwable"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"CompensateCreateOrder": {
# CreateOrder Compensation measures
"Type": "ServiceTask",
"ServiceName": "workOrderServiceImpl", # need workOrderServiceImpl Of bean
"ServiceMethod": "compensateCreateOrder", # call workOrderServiceImpl#compensateCreateOrder Method inside
"Input": [
"$.[businessKey]"
]
},
"CompensateCreateProblem": {
"Type": "ServiceTask",
"ServiceName": "workProblemApi",
"ServiceMethod": "compensateInsertWithLink",
"Input": [
"$.[businessKey]"
]
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type": "Succeed"
},
"Fail": {
"Type": "Fail",
"ErrorCode": "CREATE_FAILED",
"Message": "create order failed"
}
}
}
2、 To configure saga
Configuration information of the state machine , Like your state machine json What is the name of the document , Where is the , And injected into the spring In the container , Official examples , Everyone can see that it is xml, I'll change it here to springboot Configuration mode injection is ok , You can choose a way at will
3、 I'll change it here to springboot Of @Configuration
Injection mode , The content in it corresponds to the above xml The contents of the document ,
There's one thing to pay attention to , When the state machine is executing , Will go to spring Of bean Corresponding bean, We use dubbo The way to inject , Will not be in spring Inside the container of , So there will be no corresponding bean, But in fact, we use @DubboReference It's available , therefore , Here we manually inject .
package cn.gxm.order.config;
import cn.gxm.device.api.WorkProblemApi;
import cn.gxm.order.service.impl.WorkOrderServiceImpl;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.saga.engine.StateMachineEngine;
import io.seata.saga.engine.config.DbStateMachineConfig;
import io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine;
import io.seata.saga.rm.StateMachineEngineHolder;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import javax.sql.DataSource;
import java.io.File;
/** * @author GXM * @version 1.0.0 * @Description seata saga Mode configuration information * @createTime 2022 year 06 month 13 Japan */
@Configuration
public class SeataSagaConfig {
/** * bean The default is the method name , It's OK not to write here , But in case of explicit semantics It's better to write * * @return */
@Bean(name = "seataSagaDataSource")
public DataSource seataSagaDataSource() {
HikariDataSource dataSource = new HikariDataSource();
// The database address is seata_state_inst 、seata_state_machine_def、seata_state_machine_inst Address of three tables , Generally, it is in the database of the business initiator
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/gxm-300?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean(name = "dbStateMachineConfig")
public DbStateMachineConfig dbStateMachineConfig(@Qualifier("seataSagaDataSource") DataSource seataSagaDataSource) {
DbStateMachineConfig dbStateMachineConfig = new DbStateMachineConfig();
dbStateMachineConfig.setDataSource(seataSagaDataSource);
ClassPathResource resource = new ClassPathResource("statelang" + File.separator + "create_order_and_problem.json");
dbStateMachineConfig.setResources(new Resource[]{
resource});
dbStateMachineConfig.setEnableAsync(true);
// Execute thread ( This official document is strange , The types do not match ......) Thread pool used in event driven execution , If all state machines are executed synchronously and there is no circular task, it is unnecessary
// dbStateMachineConfig.setThreadPoolExecutor();
dbStateMachineConfig.setApplicationId("test_saga");
dbStateMachineConfig.setTxServiceGroup("my_test_tx_group");
return dbStateMachineConfig;
}
/** * saga State machine example */
@Bean(name = "stateMachineEngine")
public StateMachineEngine stateMachineEngine(@Qualifier("dbStateMachineConfig") DbStateMachineConfig dbStateMachineConfig) {
ProcessCtrlStateMachineEngine processCtrlStateMachineEngine = new ProcessCtrlStateMachineEngine();
processCtrlStateMachineEngine.setStateMachineConfig(dbStateMachineConfig);
return processCtrlStateMachineEngine;
}
/** * Seata Server This is required for transaction recovery Holder Get stateMachineEngine example * * @param stateMachineEngine * @return */
@Bean
public StateMachineEngineHolder stateMachineEngineHolder(@Qualifier("stateMachineEngine") StateMachineEngine stateMachineEngine) {
StateMachineEngineHolder stateMachineEngineHolder = new StateMachineEngineHolder();
stateMachineEngineHolder.setStateMachineEngine(stateMachineEngine);
return stateMachineEngineHolder;
}
@DubboReference
private WorkProblemApi workProblemApi;
/** * because 2.x Version of dubbo Use annotation @DubboReference when , Will not inject spring in (@DubboReference Not at all Spring Defined Bean, So it doesn't generate BeanDefinition , That is, I won't take the initiative createBean , Can only be triggered during attribute injection ), * and saga The state machine is reading * The time is from spring Get other services in bean, So here's a manual injection * Specific analysis can see https://heapdump.cn/article/3610812 * @return */
@Bean(name = "workProblemApi")
public WorkProblemApi workProblemApi() {
return workProblemApi;
}
}
3.1.4、 explain ( important )
1、 There is a problem , It needs to be explained , Is the parameter businessKey
, Each time we start a business, we get the current timestamp as businessKey
, Pass it into the business logic , This is for , Subsequent compensation , Know how to compensate , for instance , Our current business , If you fail , We must find the corresponding 4 Tabular 4 Data , And then delete , We just need to put the generated primary key id, Put it into the global object of the state machine for flow , that will do , But there's a situation , Once an exception occurs in a certain state , Then there is no data returned in the state machine , Then there is no way to id Pass in the next step , What about the compensation business in the back . therefore , There are two ways
2、 The first one is , This article does this , Business key Pass in , And the corresponding four tables , You need one businessKey Field , When the corresponding business is modified , hold businessKey Fill it up , When making compensation , Directly according to the business key To operate .
3、 The second kind , Or incoming business key, But how to add business in the corresponding table key Field , But save to redis Among such third parties , For example, the current business , When inserting the database , In an hmap,key Business key,filed Is the corresponding table name , Values are generated id, Then when it comes to compensation , According to business key from redis To take to .
3.2、 Situation test
3.2.1、 Normal condition
3.2.1.1、 State machine object analysis
1、 Let's test the cases without abnormal conditions , In the business service in , Set a breakpoint , View the data results of execution
2、 The preliminary results are as follows :
3、 Pass in the parameter
4、 Result parameters
5、 The most general implementation result
6、 State machine objects
7、 The first state machine
8、 The second state machine
3.2.1.2、 Console log
1、 Execute first CreateOrder
State machine , That is to insert order Two tables of service
2、 Re execution CreateProblem
State machine , That is to insert device
Two tables of service
3、 The total results are as follows , We focus on instance.getStatus()
and instance.getCompensationStatus()
If there is any problem .
3.2.1.3、 Database data ( Later, I have time to fill in the meaning of this part of the table )
1、order Under service gxm-300
Database situation , Of course, business table notice_info
and work_order
The data is certain , I will not put the picture
2、device Under service gxm-301
Database situation , Of course, business table work_order_problem_link
and work_problem
The data is certain , I will not put the picture
3、seata Three tables of data on the server
3.2.2、 Abnormal situation
1、 Before testing this situation , Clear the table data
truncate table `gxm-300`.notice_info;
truncate table `gxm-300`.undo_log;
truncate table `gxm-300`.work_order;
truncate table `gxm-300`.seata_state_inst;
truncate table `gxm-300`.seata_state_machine_def;
truncate table `gxm-300`.seata_state_machine_inst;
truncate table `gxm-301`.undo_log;
truncate table `gxm-301`.work_order_problem_link;
truncate table `gxm-301`.work_problem;
truncate table `seata`.branch_table;
truncate table `seata`.global_table;
truncate table `seata`.lock_table;
3.2.2.1、 State machine object analysis
1、 We are device The server threw an exception
2、 Then there was the previous log Place a breakpoint
3、 State machine objects
4、 Let's talk about this state , This state is normal ( With compensation ), See the official instructions
5、 The list of state machines is 4 individual .
3.2.2.2、 Console log
1、 Execute first CreateOrder
State machine , That is to insert order Two tables of service , No problem , Because at this time, the business is still normal .
2、 Re execution CreateProblem
State machine , That is to insert device
Two tables of service , Then an error java.lang.ArithmeticException: / by zero
3、order Service received device Error messages for
4、 Start the compensation state
5、 First compensate device service , Because it finally executes ( This picture should be on page 4 In the middle of Zhang , After execution , You can see State[CompensateCreateProblem] finish with status[SU])
6、 Recompense order service
7、order Compensation is also successful , Total results
The most general implementation result : UN; xid : 192.168.172.232:8091:279996470823649280; businessKey: 1655191560707; compensationStatus SU
3.2.2.3、 Database data ( Later, I have time to fill in the meaning of this part of the table )
1、order Under service gxm-300
Database situation , Of course, business table notice_info
and work_order
The data is Definitely not , Because it rolled back , I will not put the picture
2、device Under service gxm-301
Database situation , Of course, business table work_order_problem_link
and work_problem
The data is Definitely not , Because it rolled back , I will not put the picture
3、seata Three tables of data on the server
3.2.3、 Additional explanation
1、 According to the state language we wrote earlier json The file knows , Compensation trigger CompensationTrigger
, Is in CreateProblem
It's triggered when
2、 That's for the starting state CreateOrder
Come on , It also has 2 A local mapper, And it doesn't set a compensation trigger , Once directly in CreateOrder
What about failure , So there are two ways ,
- The first way ,
CreateOrder
Phase failure , Also directly trigger the compensation point , In this way, it is also implemented directlyCompensateCreateOrder
nothing more , Because it is compensated by flashbacks , It is the first . - The second way , Do not set it to trigger the compensation point , Use it directly spring Rollback the transaction of it , Because it is a local project 2 individual mapper.
3、 This means that the second method is set directly spring Transaction rollback , as follows
3.3、 Other questions
1、 Note that once an exception occurs in the intermediate state , Then it's hard for you to get the result of this state
3、 According to the first 2 spot , The same can be , We're setting up ServiceTask
When , It is also necessary to put abnormal judgment in the first place
Four 、XA Pattern
4.1、 Instructions
1、 Actually XA
and AT
almost , I mean, the code is almost , So there are not many changes , The main point is the database support you use XA, such as MySQL It's OK , The main point is to open the mode , The default is AT
Pattern ( Of course, this parameter seata.data-source-proxy-mode
yes 1.4.0 Start offering , Previous versions can only be switched through code modification data source agent , It is said that )
2、 First, we need to modify the proxy data source , If you're using seata-starer
, And the version seata Version of ≥1.4.0 You can directly use annotations to replace , Here's the picture ,
Before that
AT
Patterns in , Not configured , Becauseseata-starer
rely on , Built in GlobalTransactionScanner Automatic initialization function , The default is AT Pattern , So there's no need to configure
3、 But if your version doesn't ≥ 1.4.0, Then you can only use code to switch , Of course, you can choose to update directly (seata to update , Or update it separately , It's said later that )
@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
// DataSourceProxy for AT mode
// return new DataSourceProxy(druidDataSource);
// DataSourceProxyXA for XA mode
return new DataSourceProxyXA(druidDataSource);
}
4、 because XA
Mode does not use undo_log
surface , So we can delete it directly , Last gxm-300
and gxm-301
as follows
5、 Because I'm using spring-cloud-starter-alibaba-seata
rely on , Inside seata Version or 1.3.0 edition , Switch directly without using that annotation AT
and XA
Pattern , If I use code change , You have to go from the data source to mapper, Change it all over again , There's really some trouble , therefore , We can manually lift seata
Version of , Of course, the official website also has suggestions , You can prompt the version in the following way , So I'm here hold order
Service and device
Service seata
Manually lift to 1.4.0 edition .
4.2、 Code changes
1、 stay device
Service and order
Add data source agent configuration to the service ( Use comments or code , Look at your version or you want to use that )
2、 Others are like AT
There is no difference in the mode
3、 If the project has no performance requirements, I suggest using XA Pattern , because , It is strong consistency , and AT
Pattern is the most general consistency . Explanation , Look at section five , How to choose four modes .
4.3、 Normal test ( Reference resources AT Pattern )
Omit
4.4、 Abnormal test ( Reference resources AT Pattern )
Omit
4.5、 test seata When rolling back , After the mirrored data is modified by other transactions , Unable to rollback successful cases ( Reference resources AT Pattern )
1、 We're still with AT
Model as , Add an interface , Modify the inserted data
2、 And in device
Service hibernation
3、 Remember to modify the global transaction time and the timeout time of the remote calling component ,AT
Pattern has , No more talk here
4、 stay device service
Sleep time , We call to change the interface , You will find that it has been blocking , Wait until the plug-in interface is over , It also returns , And look at the console data , It is found that the data has not been modified , But isn't the log insert statement executed first . That's the sum of AT
The difference between the modes .XA
as follows
1、 because XA Data will not be submitted in the first stage , Will lock that resource to the second stage ( You go to the database during its sleep , You can't see the inserted data ), After the completion of the first phase , Call to modify the interface , yes Can't find That data .
2、 andAT
The pattern is submitted directly in the first stage , So you can find that data , Subsequent failure rollback is based on undo_log Mirror data to rollback , SoAT
Pattern is the most general consistency , andXA
Patterns are strongly consistent .
5、 ... and 、 How to choose four modes ( It is strongly recommended to look at )
1、 The advantages and disadvantages of the four modes and what we need to deal with , This article all says Distributed transactions ——Seata、XA、TCC、AT、SAGA Pattern
6、 ... and 、 Problems encountered
6.1、Cannot construct instance of java.time.LocalDateTime
1、 Many people have encountered this problem ,github Of issues It is also mentioned above , The main reason is seata When rolling back , be used undo_log Image data , By default, the mirrored data is fastjson Serialized , Then if your business table has a time field , And is datetime
type , that seata When rolling back such data , Will be affected . such as , My current business table notice_info
There is this time field , Once the rollback of this business is involved , Want to go undo_log
Find the front and back mirrored data of the previous table in the table of , It will fail in deserialization .
2、 This is the content of the image , You can see that there is indeed this time field .
3、 When this problem arises , At the global transaction initiator , That is to use @GlobalTransactional
Annotated Services Unlimited error reporting , Constantly reporting errors , You can see the picture below , I turned that service off , Otherwise, keep refreshing that error .
4、 Solution , Finally, I used to reduce MySQL Version to 8.0.20
seata appear json Time field serialization error problem , I don't recommend changing serialization to other methods , Because of the readability of other ways , Not so good , In case of online problems , We need to check in time , still json Convenient and quick .
Official github There is also this problem above LocalDateTime Convert exceptions ,springboot edition :2.4.4
6.2、io.seata.core.exception.RmTransactionException: Response[ TransactionException[branch register request failed. xid=xx, msg=Data truncation: Data too
1、 A screenshot of the problem is shown below
2、 But according to the log above, you can't see anything , It's just that the data is big , After searching online , You will find that this is because lock_table
Table when inserting data , The field is too long .
3、 So there is a problem with the field of that table , Don't make random changes based on the Internet , Look at the server log , Because the client did not say the field of that table ,seata
The server logs are as follows , But it doesn't seem to say that table , Just say PK
Field , therefore , If you know something seata Streamline the operation process , I knew it was lock_table
Tabular pk
Field
4、 therefore , So let's revise that lock_table
Tabular pk
Field length is enough .
6.3、saga State machine not found dubbo Of bean
1、 We go through @BubboReference Yes. , But when the state machine executes , Can't find
2、 The reason is because 2.x Version of dubbo Use annotation @DubboReference when , Will not inject spring in (@DubboReference Not at all Spring Defined Bean, So it doesn't generate BeanDefinition , That is, I won't take the initiative createBean , Can only be triggered during attribute injection ), and saga The state machine is reading The time is from spring Get other services in bean, So here's a manual injection Specific analysis can see https://heapdump.cn/article/3610812
3、 The solution is to manually inject into spring Of bean In the container .
6.4、XA Appears in mode java.lang.NoSuchMethodException: com.mysql.cj.conf.PropertySet.getBooleanReadableProperty(java.lang.String)
4、 The reason is that seata The default is DruidDataSource
Database connection pool , and DruidDataSource
Inside util In bag MySqlUtils Class createXAConnection Method , Will use MySQL Driven getBooleanReadableProperty
Method , But the higher version MySQL There is no such method in the driver , So wrong reporting , I will directly lower here MySQL The driver version is ok , take mysql The drive package version is switched to 8.0.11, In this version ,getBooleanReadableProperty(String) Methods still exist .
This problem ,github It is also mentioned above ,
边栏推荐
- Unable to open kernel device '\.\vmcidev\vmx': operation completed successfully. Reboot after installing vmware workstation? Module "devicepoweron" failed to start. Failed to start the virtual machine
- P1031 [noip2002 improvement group] average Solitaire
- A simple example of delegate usage
- SQL Server knowledge collection 11: Constraints
- CAS mechanism
- Unity script visualization about layout code
- Is the soft test intermediate useful??
- Unity downloads files through the server address
- I plan to take part in security work. How about information security engineers and how to prepare for the soft exam?
- About hzero resource error (groovy.lang.missingpropertyexception: no such property: weight for class)
猜你喜欢
Find the root of equation ax^2+bx+c=0 (C language)
[pro test feasible] error while loading shared libraries solution
Operation method of Orange Pie orangepi 4 lts development board connecting SATA hard disk through mini PCIe
Multithreaded asynchronous orchestration
Elegant controller layer code
Mysql的json格式查询
【机器学习 03】拉格朗日乘子法
When do you usually get grades in the soft exam? Online pedaling?
软考中级,软件设计师考试那些内容,考试大纲什么的?
软考一般什么时候出成绩呢?在线蹬?
随机推荐
"Dream Cup" 2017 Jiangsu information and future primary school summer camp it expert PK program design questions
【OneNote】无法连接到网络,无法同步问题
Application of OpenGL gllightfv function and related knowledge of light source
Get pictures through opencv, change channels and save them
July 10, 2022 "five heart public welfare" activity notice + registration entry (two-dimensional code)
Gym installation pit records
Is the soft test intermediate useful??
Leetcode-560: subarray with sum K
Trajectory planning for multi robot systems: methods and Applications Overview reading notes
如何顺利通过下半年的高级系统架构设计师?
Transaction rolled back because it has been marked as rollback-only解决
The gun startles the dragon, and the crowd "locks" Zhou Zhi
中级网络工程师是什么?主要是考什么,有什么用?
2022.7.6DAY598
施努卡:机器视觉定位技术 机器视觉定位原理
shardingsphere分库分表示例(逻辑表,真实表,绑定表,广播表,单表)
简单易修改的弹框组件
ArrayList thread insecurity and Solutions
Laya common script commands
南航 PA3.1