PHP 微服务之 [分布式事物]

2019-05-18 18:50:45 +08:00
 tanszhe

分布式事物一直是微服务的一个难点。相关的解决方案和框架大部分是 java 的,那么 php 该如何解决呢?下面一步一步讲解如何用 php 解决分布式事物。

单机单数据源事物

首先从单机事物开始。

大概逻辑如下 :


try {  
  // 开始事物
  $db->beginTransaction();
  
  // 执行你的操作 
  // ...
  
  // 提交事物
  $db->commit();
 
} catch (Exception $e) {

  // 执行失败 回滚
  $db->rollBack();
  
}

单机多个数据源事物

如果你业务涉及到多个数据库,事物大概逻辑是这个样子:


try {  
  // 开始事物
  $db1->beginTransaction();
  $db2->beginTransaction();
  
  // 执行你的操作 
  // ...
  
  // 提交事物
  $db1->commit();
  $db2->commit();
 
} catch (Exception $e) {

  // 执行失败 回滚
  $db1->rollBack();
  $db2->rollBack();
  
}

多机多数据源事物(分布式事物)

如果你的数据源和业务代码都是分开的(微服务)这就是我们今天的核心。 由前面两种情况来看,大概逻辑是差不多的,主要也分为 4 个步骤。

  1. 开始事物
  2. 执行逻辑代码
  3. 提交事物
  4. 回滚事物

有些文章也称为tcc也就是 234 步骤。

我们用一个常用的例子:下单。
主要 3 个步骤:

  1. 创建订单
  2. 修改库存
  3. 修改用户积分

假设订单,库存,用户都是独立的服务。

按照前面的经验大概分为 4 个步骤,我们以用户为例 代码如下:

class User
{
	// 开始事物
	public function beginTransaction()
	{
		$db->beginTransaction();
		return $this;
	}
	
	// 执行代码
	public function doTransaction()
	{
		// 执行你的操作 
  		// ...
  		return $this;
	}
	
	public function commit()
	{
		$db->commit();
	}
	
	public funtion rollBack()
	{
		$db->rollBack();
	}

}

库存(stock),订单(order)和上面类似,也需要这 4 个方法,我就不写了。 难点在于我们没法直接操作数据源,只能通过 rpc 调用相应的服务来操作。依次执行上面的方法就好了。代码如下:

try {  
  // 开始事物
  $user = new User();
  $stock = new Stock();
  $order = new Order();
  
  $user = $user->beginTransaction();
  $stock = $stock->beginTransaction();
  $order = $order->beginTransaction();
  
  
  // 执行你的操作 
  $user = $user->doTransaction();
  $stock = $stock->doTransaction();
  $order = $order->doTransaction();
  
  // 提交事物
  $user->commit();
  $stock->commit();
  $order->commit();
 
} catch (Exception $e) {

  // 执行失败 回滚
  $user->rollBack();
  $stock->rollBack();
  $order->rollBack();
  
}

到这里可能有人看出问题来了,正常情况下这样肯定是不行的。要上面这段代码成立需要满足 1 个条件:User分别调用了 3 次,也就是 3 个请求。要保证这 3 个请求是调用的同一个实例化后的对象。StockOrder一样。

User 调用逻辑如下:


// 第一次请求调用
$user = new User();
$user = $user->beginTransaction();

// 第二次请求调用 复用的第一次 $user
$user = $user->doTransaction();

// 第三次请求调用 复用的第一次 $user
$user->commit();
//或者 
$user->rollBack();


注意: 虽然调用了 3 次但是只new了一次, 第二次和第三次请求是复用的第一次的对象。要满足这个条件 服务供方必须 常驻内存 ,而且提供的rpc 服务必须支持链式调用的功能。

one框架 https://github.com/lizhichao/one
极简 . 高性能 . 松耦合 . 分布式 . 可运行于多种环境

one框架完美支持上面的要求。只需要把上面的UserStockOrder添加为 rpc 服务即可。还需要注意beginTransactiondoTransaction方法必须返回$this提供给后面的方法调用。

user 服务如下:

RpcServer::add(User::class);

其他两个类似。到此分布式事物问题就搞定了,可能觉得这么简单吗?这主要由于one 框架的 rpc 服务提供了链式调用(多个请求复用同一个对象)的功能。

可能有人要问:如果因为网络问题或者其他问题导致最后一个服务的最后一次调用失败了怎么办? 解决方案就是 事物补偿,你可以把这类极端的情况下的错误,放到一个队列里 起一服务来专门处理这里问题。

5206 次点击
所在节点    PHP
25 条回复
CoderGeek
2019-05-19 02:42:06 +08:00
上周刚自己玩了玩 lcn
xuanbg
2019-05-19 08:34:41 +08:00
根本就不存在“分布式事务”这种东西,不光 PHP 没有,Java 生态里面也没有。有的只是分布式系统数据一致性解决方案!注意,只有解决方案,没有现成可用的框架和代码,所有的一切都需要自己根据自己的业务去实现。
m939594960
2019-05-19 10:13:26 +08:00
@eslizn #20 文档上说过么?我看文档有专门协程写文件的啊?
AngryPanda
2019-05-19 22:03:36 +08:00
楼主你是来黑 PHP 的吧
ChoateYao
2019-05-22 20:27:38 +08:00
技术果然是通用的,啰嗦了一大堆还不如在开头写一句参考 JTA 事物。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/565389

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX