introduce
dtm/dtm-client Is a distributed transaction manager DTM The PHP client of has supported the distributed transaction mode of TCC mode, Saga mode and two-stage message mode, and has realized the communication with DTM Server by HTTP protocol or gRPC protocol respectively. The client can run safely in the environment of PHP FPM and Swoole Hyperf Made more easy-to-use functional support.
About DTM
DTM is an open source distributed transaction manager based on Go language, which provides the powerful function of combining transactions across languages and storage engines. DTM gracefully solves the problems of idempotent, null compensation, suspension and other distributed transactions, and also provides a simple, easy-to-use, high-performance and easy horizontal expansion distributed transaction solution.
Highlights
- Easy to use
- The zero configuration startup service provides a very simple HTTP interface, which greatly reduces the difficulty of starting distributed transactions
- Cross language
- It can be used by companies with multi language stack. It is convenient to use Go, Python, PHP, NodeJs, Ruby, C# and other languages.
- Easy to use
- Developers no longer worry about suspension, null compensation, idempotent and other problems, and pioneered the sub transaction barrier technology to handle them on their behalf
- Easy to deploy and expand
- Relying only on MySQL/Redis, it is easy to deploy, cluster and expand horizontally
- Multiple distributed transaction protocol support
- TCC, SAGA, XA, two-stage message, one-stop solution to a variety of distributed transaction problems
contrast
In the non Java language, we haven't seen any mature distributed transaction manager except DTM yet. Therefore, here we compare DTM with the most mature open source project in Java, Seata:
characteristic | DTM | SEATA | remarks |
---|---|---|---|
Support language | Go,C#,Java,Python,PHP... | Java | DTM can easily access a new language |
Storage engine | Support database, Redis, Mongo, etc | database | |
exception handling | Sub transaction barrier automatic processing | Manual processing | DTM solves idempotent, suspension and null compensation |
SAGA transaction | Very easy to use | Complex state machine | |
Phase II message | ✓ | ✗ | Minimal message final consistency architecture |
TCC transaction | ✓ | ✓ | |
XA transaction | ✓ | ✓ | |
AT transaction | XA is recommended | ✓ | AT is similar to XA, but has dirty rollback |
Single service multiple data sources | ✓ | ✗ | |
communication protocol | HTTP,gRPC | Dubbo et al | DTM is more cloud friendly |
Number of star s | DTM released version 0.1 from June 4, 2021, and developed rapidly |
From the features compared above, DTM has great advantages in many aspects. If you consider multi language support and multi storage engine support, DTM is undoubtedly your first choice
install
It is very convenient to install DTM client through Composer
composer require dtm/dtm-client
- Don't forget to start DTM Server when using
to configure
configuration file
If you are using it in the Hyperf framework, after installing the component, you can publish a configuration file in. Through the following vendor:publish command/ config/autoload/dtm.php
php bin/hyperf.php vendor:publish dtm/dtm-client
If you are using in a non Hyperf frame, you can copy it/ vendor/dtm/dtm-client/publish/dtm.php file to the corresponding configuration directory.
use DtmClient\Constants\Protocol; use DtmClient\Constants\DbType; return [ // The communication protocol between the client and DTM Server supports Protocol::HTTP and Protocol::GRPC 'protocol' => Protocol::HTTP, // Address of DTM Server 'server' => '127.0.0.1', // Port of DTM Server 'port' => [ 'http' => 36789, 'grpc' => 36790, ], // Sub transaction barrier configuration 'barrier' => [ // Sub transaction barrier configuration in DB mode 'db' => [ 'type' => DbType::MySQL ], // Sub transaction barrier configuration in Redis mode 'redis' => [ // Timeout of sub transaction barrier record 'expire_seconds' => 7 * 86400, ], // Classes that apply sub transaction barriers under non Hyperf framework 'apply' => [], ], // General configuration of Guzzle client under HTTP protocol 'guzzle' => [ 'options' => [], ], ];
Configuration Middleware
Before use, you need to configure DtmClient\Middleware\DtmMiddleware middleware as the global middleware of the Server. The middleware supports PSR-15 specification and can be applied to various frameworks that support the specification.
Refer to the middleware configuration in Hyperf Hyperf document - Middleware A chapter.
use
The use of DTM client is very simple. We provide a sample project dtm-php/dtm-sample To help you better understand and debug.
Before using this component, it is also strongly recommended that you read DTM official document , for a more detailed understanding.
TCC mode
TCC mode is a very popular flexible transaction solution, which is composed of the acronyms of try confirm cancel. The concept of TCC was first proposed in a paper entitled "Life beyond Distributed Transactions:an Apostate's Opinion" published by Pat Helland in 2007.
Three stages of TCC
Try stage: try to execute, complete all business checks (consistency), and reserve necessary business resources (quasi isolation)
Confirm phase: if the Try of all branches is successful, go to the confirm phase. Confirm really executes the business, does not make any business inspection, and only uses the business resources reserved in the Try stage
Cancel phase: if one Try of all branches fails, go to the cancel phase. Cancel releases the business resources reserved in the Try phase.
If we want to conduct a business similar to inter-bank transfer, the transfer out and transfer in are in different micro services respectively. The typical sequence diagram of a successfully completed TCC transaction is as follows:
Code example
The following shows how to use it in the Hyperf framework, which is similar to other frameworks
<?php namespace App\Controller; use DtmClient\TCC; use DtmClient\TransContext; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\GetMapping; use Throwable; #[Controller(prefix: '/tcc')] class TccController { protected string $serviceUri = 'http://127.0.0.1:9501'; #[Inject] protected TCC $tcc; #[GetMapping(path: 'successCase')] public function successCase() { try { $this->tcc->globalTransaction(function (TCC $tcc) { // Create call data for sub transaction A $tcc->callBranch( // Parameters for calling the Try method ['amount' => 30], // URL of Try method $this->serviceUri . '/tcc/transA/try', // URL of Confirm method $this->serviceUri . '/tcc/transA/confirm', // URL of the Cancel method $this->serviceUri . '/tcc/transA/cancel' ); // Create the call data of sub transaction B, and so on $tcc->callBranch( ['amount' => 30], $this->serviceUri . '/tcc/transB/try', $this->serviceUri . '/tcc/transB/confirm', $this->serviceUri . '/tcc/transB/cancel' ); }); } catch (Throwable $e) { var_dump($e->getMessage(), $e->getTraceAsString()); } // Get the global transaction ID through TransContext::getGid() and return it return TransContext::getGid(); } }
Saga mode
Saga mode is one of the most famous solutions in the field of distributed transactions, and it is also very popular in major systems. It first appeared in a paper published by Hector garcaa molrna & Kenneth Salem in 1987 SAGAS Inside.
Saga is a final consistent transaction and a flexible transaction, also known as long running transaction. Saga is composed of a series of local transactions. After each local transaction updates the database, it will publish a message or an event to trigger the execution of the next local transaction in Saga global transaction. If a local transaction fails because some business rules cannot be met, saga will perform the compensation operation of all transactions successfully committed before the failed transaction. Therefore, when saga mode is compared with TCC mode, due to the lack of resource reservation steps, it often becomes more troublesome when implementing rollback logic.
Saga sub transaction splitting
For example, we want to carry out A business similar to bank inter-bank transfer, transferring 30 yuan in account A to account B. according to the principle of Saga transaction, we divide the whole global transaction into the following services:
- For TransOut service, operation will be carried out here, and 30 yuan will be deducted from account A
- The transfer out compensation service rolls back the above transfer out operation, that is, account A is increased by 30 yuan
- Transfer into the (TransIn) service, where the B account will be increased by 30 yuan
- The transfer out compensation service rolls back the above transfer in operation, that is, the B account is reduced by 30 yuan
The logic of the whole transaction is:
Execute transfer out successfully = > execute transfer in successfully = > global transaction completed
If an error occurs in the middle, such as an error in transferring to account B, the compensation operation of the executed branch will be called, that is:
Transfer out successful = > transfer in failed = > transfer in compensation successful = > transfer out compensation successful = > global transaction rollback completed
The following is a typical sequence diagram of a SAGA transaction completed successfully:
Code example
The following shows how to use it in the Hyperf framework, which is similar to other frameworks
namespace App\Controller; use DtmClient\Saga; use DtmClient\TransContext; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\GetMapping; #[Controller(prefix: '/saga')] class SagaController { protected string $serviceUri = 'http://127.0.0.1:9501'; #[Inject] protected Saga $saga; #[GetMapping(path: 'successCase')] public function successCase(): string { $payload = ['amount' => 50]; // Initialize Saga transaction $this->saga->init(); // Add transfer out sub transaction $this->saga->add( $this->serviceUri . '/saga/transOut', $this->serviceUri . '/saga/transOutCompensate', $payload ); // Add transfer in sub transaction $this->saga->add( $this->serviceUri . '/saga/transIn', $this->serviceUri . '/saga/transInCompensate', $payload ); // Commit Saga transaction $this->saga->submit(); // Get the global transaction ID through TransContext::getGid() and return it return TransContext::getGid(); } }