3

https://github.com/dtm-php/dtm-client

introduce

dtm/dtm-client is the PHP client of the distributed transaction manager DTM . It already supports the distributed transaction modes of TCC mode, Saga and two-phase message mode, and realizes the communication with DTM Server through HTTP protocol or gRPC respectively. Protocol communication, the client can safely run in the PHP-FPM and Swoole coroutine environment, and has made more easy-to-use functional support for Hyperf .

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 elegantly solves distributed transaction problems such as idempotency, null compensation, and suspension, and also provides a distributed transaction solution that is easy to use, high-performance, and easy to scale horizontally.

Highlights

  • Very easy to use

    • Start the service with zero configuration and provide a very simple HTTP interface, which greatly reduces the difficulty of getting started with distributed transactions
  • cross language

    • Suitable for companies with multiple language stacks. It is convenient to use in various languages such as Go, Python, PHP, NodeJs, Ruby, C#, etc.
  • Simple to use

    • Developers no longer worry about suspension, null compensation, idempotent and other problems, and the first sub-transaction barrier technology handles it for you
  • Easy to deploy and expand

    • Only rely on MySQL/Redis, easy to deploy, easy to cluster, easy to scale horizontally
  • Multiple distributed transaction protocol support

    • TCC, SAGA, XA, two-stage message, one-stop solution to various distributed transaction problems

Compared

In non-Java languages, there is no mature distributed transaction manager other than DTM, so here is a comparison between DTM and Seata, the most mature open source project in Java:

characteristicDTMSEATARemark
Supported language <span style="color:green">Go、C#、Java、Python、PHP...</span><span style="color:orange">Java</span>DTM allows easy access to a new language
Storage Engine <span style="color:green">Support database, Redis, Mongo, etc.</span><span style="color:orange">Database</span>
Exception Handling <span style="color:green"> Subtransaction barriers are handled automatically</span><span style="color:orange">Manual processing</span>DTM solves idempotent, hanging, null compensation
SAGA Transaction <span style="color:green">Very easy to use</span><span style="color:orange">Complex state machine</span>
Two-phase message <span style="color:green">✓</span><span style="color:red">✗</span>Minimal Message Eventual Consistency Architecture
TCC Transaction <span style="color:green">✓</span><span style="color:green">✓</span>
XA transaction <span style="color:green">✓</span><span style="color:green">✓</span>
AT Transaction <span style="color:orange">XA is recommended</span><span style="color:green">✓</span>AT is similar to XA, but with dirty rollback
Single Service Multiple Data Sources <span style="color:green">✓</span><span style="color:red">✗</span>
communication protocol HTTP、gRPCProtocols such as DubboDTM is more friendly to cloud native
star quantity <img src="https://cdn.learnku.com/uploads/images/202202/15/21058/zh9c3GAmYq.svg" alt="github stars"/><img src="https://cdn.learnku.com/uploads/images/202202/15/21058/pn5f7zYuSG.svg" alt="github stars"/>DTM released version 0.1 from 2021-06-04, developing rapidly

From the characteristics of the comparison 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 you use it

configure

configuration file

If you are using the Hyperf framework, after installing the component, you can publish a configuration file on ./config/autoload/dtm.php by the following vendor:publish command

php bin/hyperf.php vendor:publish dtm/dtm-client

If you are using it in a non-Hyperf framework, you can copy the ./vendor/dtm/dtm-client/publish/dtm.php file to the corresponding configuration directory.

use DtmClient\Constants\Protocol;
use DtmClient\Constants\DbType;

return [
    // 客户端与 DTM Server 通讯的协议,支持 Protocol::HTTP 和 Protocol::GRPC 两种
    'protocol' => Protocol::HTTP,
    // DTM Server 的地址
    'server' => '127.0.0.1',
    // DTM Server 的端口
    'port' => [
        'http' => 36789,
        'grpc' => 36790,
    ],
    // 子事务屏障配置
    'barrier' => [
        // DB 模式下的子事务屏障配置
        'db' => [
            'type' => DbType::MySQL
        ],
        // Redis 模式下的子事务屏障配置
        'redis' => [
            // 子事务屏障记录的超时时间
            'expire_seconds' => 7 * 86400,
        ],
        // 非 Hyperf 框架下应用子事务屏障的类
        'apply' => [],
    ],
    // HTTP 协议下 Guzzle 客户端的通用配置
    'guzzle' => [
        'options' => [],
    ],
];

configure middleware

Before using it, you need to configure the DtmClient\Middleware\DtmMiddleware middleware as the global middleware of the Server. This middleware supports the PSR-15 specification and can be applied to every framework that supports this specification.
Middleware configuration in Hyperf can refer to Hyperf Documentation - Middleware 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

The TCC mode is a very popular flexible transaction solution. The concept of TCC is composed of the acronyms of the three words Try-Confirm-Cancel. It was first published by Pat Helland in 2007. Transactions: an Apostate's Opinion" paper.

3 stages of TCC

Try phase: try to execute, complete all business checks (consistency), reserve necessary business resources (quasi-isolation)
Confirm stage: If the Try of all branches is successful, go to the Confirm stage. Confirm actually executes the business without any business check, and only uses the business resources reserved in the Try phase
Cancel stage: If one of the Try of all branches fails, go to the Cancel stage. Cancel releases the business resources reserved in the Try phase.

If we want to conduct a business similar to inter-bank transfer between banks, the transfer out (TransOut) and the transfer in (TransIn) are in different microservices, and a typical sequence diagram of a successfully completed TCC transaction is as follows:

<img src="https://dtm.pub/assets/tcc_normal.dea14fb3.jpg" height=600 />

code example

The following shows how to use it in the Hyperf framework, other frameworks are similar

<?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) {
                // 创建子事务 A 的调用数据
                $tcc->callBranch(
                    // 调用 Try 方法的参数
                    ['amount' => 30],
                    // Try 方法的 URL
                    $this->serviceUri . '/tcc/transA/try',
                    // Confirm 方法的 URL
                    $this->serviceUri . '/tcc/transA/confirm',
                    // Cancel 方法的 URL
                    $this->serviceUri . '/tcc/transA/cancel'
                );
                // 创建子事务 B 的调用数据,以此类推
                $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());
        }
        // 通过 TransContext::getGid() 获得 全局事务ID 并返回
        return TransContext::getGid();
    }
}

Saga Mode

The Saga pattern is one of the most well-known solutions in the field of distributed transactions, and it is also very popular in major systems. It first appeared in the paper SAGAS published by Hector Garcaa-Molrna & Kenneth Salem in 1987.

Saga is an eventual consistency transaction, also a flexible transaction, also known as a long-running transaction (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 the Saga global transaction. If a local transaction fails because some business rules cannot be satisfied, Saga performs compensating actions for all transactions that were successfully committed before the failed transaction. Therefore, when the Saga mode is compared with the TCC mode, it often becomes more troublesome to implement the rollback logic due to the lack of resource reservation steps.

Saga subtransaction split

For example, we want to carry out a business similar to inter-bank transfer between banks, and transfer 30 yuan in account A to account B. According to the principle of Saga transaction, we will split the entire global transaction into the following services:

  • Transfer out (TransOut) service, the operation will be performed here. Account A will deduct 30 yuan
  • Transfer out compensation (TransOutCompensate) service, roll back the above transfer out operation, that is, increase the A account by 30 yuan
  • Transfer in (TransIn) service, here will be an increase of 30 yuan for the B account
  • Transfer out compensation (TransInCompensate) service, roll back the above transfer in operation, that is, the B account is reduced by 30 yuan

The logic of the entire transaction is:

Execute the transfer out successfully => Execute the transfer in successfully => the global transaction is completed

If an error occurs in the middle, such as an error in transferring to the B account, the compensation operation of the executed branch will be called, namely:

Execute transfer out successfully => execute transfer in failure => execute transfer in compensation successfully => execute transfer out compensation successfully => global transaction rollback completed

The following is a typical sequence diagram of a successfully completed SAGA transaction:

<img src="https://dtm.pub/assets/saga_normal.a2849672.jpg" height=428 />

code example

The following shows how to use it in the Hyperf framework, other frameworks are similar

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];
        // 初始化 Saga 事务
        $this->saga->init();
        // 增加转出子事务
        $this->saga->add(
            $this->serviceUri . '/saga/transOut', 
            $this->serviceUri . '/saga/transOutCompensate', 
            $payload
        );
        // 增加转入子事务
        $this->saga->add(
            $this->serviceUri . '/saga/transIn', 
            $this->serviceUri . '/saga/transInCompensate', 
            $payload
        );
        // 提交 Saga 事务
        $this->saga->submit();
        // 通过 TransContext::getGid() 获得 全局事务ID 并返回
        return TransContext::getGid();
    }
}

huangzhhui
2.3k 声望564 粉丝

Creator of Hyperf