vhnnnbj / laravel-rt
用于调用远程API服务的分布式事务
Requires
- php: ^7.1|^8.0
README
RT(重置事务)模式可以作为调用远程API服务的分布式事务
概述
安装版本介于laravel5.5-laravel8之间,然后安装composer包
## Composer2 version must be used
composer require windawake/laravel-reset-transaction dev-master
首先创建订单、存储、账户3个MySQL数据库实例,3个控制器,3个模型,将测试套件Transaction添加到phpunit.xml中,然后启动web服务器。这些操作只需要执行以下命令即可完成
php artisan resetTransact:create-examples && php artisan serve --host=0.0.0.0 --port=8000
在另一个终端中,使用端口8001启动web服务器
php artisan serve --host=0.0.0.0 --port=8001
最后运行测试脚本 ./vendor/bin/phpunit --testsuite=Transaction --filter=ServiceTest
运行结果如下,3个示例已通过测试。
DESKTOP:/web/linux/php/laravel/laravel62# ./vendor/bin/phpunit --testsuite=Transaction --filter=ServiceTest Time: 219 ms, Memory: 22.00 MB OK (3 tests, 12 assertions)
特性
- 开箱即用,无需重构原始项目的代码,与MySQL事务写入一致,简单易用。
- 遵循两阶段提交协议,这是一个强一致性事务。在高并发情况下,它支持读提交事务的隔离级别,数据一致性接近100%的MySQL xa。
- 由于事务被分割成多个,成为几个小事务,压力测试发现死锁比MySQL普通事务更少。
- 支持分布式事务嵌套,与savepoint一致。
- 支持避免不同业务代码并发引起的脏数据问题。
- 默认支持http协议的服务端接口。如果需要支持其他协议,需要重写中间件。
- 支持子服务的嵌套分布式事务(全球首创).
- 支持服务,本地事务和分布式事务的混合嵌套(全球首创)
- 支持3次超时重试,重复请求确保幂等性
- 支持go、java语言(开发中)
原理
看过电影《明日边缘》后,你会了解归档和读取文件的操作。这个分布式事务组件模仿了电影《明日边缘》的原则。重置意味着重置,即在每个请求开始时重新读取基本服务的基本文件,然后继续后续操作。最后,所有操作回滚并归档,最后一步成功执行所有归档。整个过程遵循两阶段提交协议,首先准备,然后提交。
以用户A使用招商银行卡向用户B的招商银行账户转账100元为例,以下流程图。 在右侧图片中,重置分布式事务开启后,比左侧图片多4个请求。请求4所做的是请求1-3之前所做的,然后回到原点并重新开始,最后提交事务,结束转账过程。
支持子服务的嵌套分布式事务(全球首创)
一个世界级的问题:服务A提交->B服务回滚->C服务提交->D服务提交sql。在这种情况下,ABCD都是不同的数据库。我该如何让A服务提交B服务并回滚?C服务和D服务的所有操作怎么办?
既不能解决此问题的seata也不能解决。解决问题的关键是C服务和D服务必须提交错误,不能提交正确。如果提交了,将无法恢复。
实现支持子服务的嵌套分布式事务有哪些好处?你可以让A服务成为其他服务的服务,并且它可以任意嵌套在任何层的链接中。打破之前的束缚:服务A必须是根服务,如果服务A要成为子服务,则必须更改代码。如果使用RT模式,服务A可以成为他人的服务而无需更改代码。
如何使用
以vendor/windawake/laravel-reset-transaction/tests/ServiceTest.php
文件为例
<?php namespace Tests\Transaction; use App\Models\ResetAccountModel; use Tests\TestCase; use Illuminate\Support\Facades\DB; use App\Models\ResetOrderModel; use App\Models\ResetStorageModel; use GuzzleHttp\Client; use Laravel\ResetTransaction\Facades\RT; class ServiceTest extends TestCase { private $baseUri = 'http://127.0.0.1:8000'; /** * @var \GuzzleHttp\Client */ private $client; protected function setUp(): void { parent::setUp(); DB::setDefaultConnection('service_order'); //默认是订单服务 $this->client = new Client([ 'base_uri' => $this->baseUri, 'timeout' => 60, ]); $requestId = session_create_id(); session()->put('rt_request_id', $requestId); } public function testCreateOrderWithCommit() { $orderCount1 = ResetOrderModel::count(); $storageItem1 = ResetStorageModel::find(1); $accountItem1 = ResetAccountModel::find(1); // 开启RT模式分布式事务 $transactId = RT::beginTransaction(); $orderNo = rand(1000, 9999); // 随机订单号 $stockQty = 2; // 占用2个库存数量 $amount = 20.55; // 订单总金额20.55元 ResetOrderModel::create([ 'order_no' => $orderNo, 'stock_qty' => $stockQty, 'amount' => $amount ]); // 请求库存服务,减库存 $requestId = session_create_id(); $response = $this->client->put('/api/resetStorage/1', [ 'json' => [ 'decr_stock_qty' => $stockQty ], 'headers' => [ 'rt_request_id' => $requestId, 'rt_transact_id' => $transactId, ] ]); $resArr1 = $this->responseToArray($response); $this->assertTrue($resArr1['result'] == 1, 'lack of stock'); //返回值是1,说明操作成功 // 请求账户服务,减金额 $requestId = session_create_id(); $response = $this->client->put('/api/resetAccount/1', [ 'json' => [ 'decr_amount' => $amount ], 'headers' => [ 'rt_request_id' => $requestId, 'rt_transact_id' => $transactId, ] ]); $resArr2 = $this->responseToArray($response); $this->assertTrue($resArr2['result'] == 1, 'not enough money'); //返回值是1,说明操作成功 // 提交RT模式分布式事务 RT::commit(); $orderCount2 = ResetOrderModel::count(); $storageItem2 = ResetStorageModel::find(1); $accountItem2 = ResetAccountModel::find(1); $this->assertTrue(($orderCount1 + 1) == $orderCount2); //事务内创建了一个订单 $this->assertTrue(($storageItem1->stock_qty - $stockQty) == $storageItem2->stock_qty); //事务内创建订单后需要扣减库存 $this->assertTrue(($accountItem1->amount - $amount) == $accountItem2->amount); //事务内创建订单后需要扣减账户金额 } private function responseToArray($response) { $contents = $response->getBody()->getContents(); return json_decode($contents, true); } }
联系方式
扫描二维码进入微信群。我希望更多的朋友可以相互学习,共同研究分布式事务的知识。