分散式事務一直是微服務的一個難點。相關的解決方案和框架大部分是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個步驟。
- 開始事務
- 執行邏輯程式碼
- 提交事務
- 回滾事務
有些文章也稱為tcc
也就是 234 步驟。
我們用一個常用的例子:下單。
主要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個請求是呼叫的同一個例項化後的物件。Stock
和Order
一樣。
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
框架完美支援上面的要求。只需要把上面的User
、Stock
和Order
新增為rpc服務即可。還需要注意beginTransaction
和doTransaction
方法必須返回$this
提供給後面的方法呼叫。
user服務如下:
RpcServer::add(User::class);
其他兩個類似。到此分散式事務問題就搞定了,可能覺得這麼簡單嗎?這主要由於one框架
的rpc服務提供了鏈式呼叫(多個請求複用同一個物件)的功能。
可能有人要問:如果因為網路問題或者其他問題導致最後一個服務的最後一次呼叫失敗了怎麼辦?
解決方案就是事務補償
,你可以把這類極端的情況下的錯誤,放到一個佇列裡 起一服務來專門處理這裡問題。