iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Modern Web

30 天上手! PHP 微服務入門與開發系列 第 24

第二十四章、Anser-Saga:重新執行被中斷的協作器快照 - PHP 微服務入門與開發

  • 分享至 

  • xImage
  •  

在動態且複雜的協作器實作中,面對各式各樣的異常情況及不可預期的錯誤是在所難免的。在前幾章節中,我們已經探討了利用 Anser-Saga 的高可用性元件,對被中斷的協作器快照進行補償。

然而,單純的補償協作器並不總是最有效或最合適的解決方案,特別是在某些複雜的業務邏輯或長期執行的交易,這麼輕易地就放棄這些協作器一件很可惜的事情。若在我們清楚了解錯誤為何發生,或是錯誤相對單純的情況下,使協作器能夠重新執行,可能是一個更為理想的選擇。

實作

與上一章一樣,我們對被放入 exit 的協作器進行請求。

再前往 Redis 查看,你應該能看到被備份的協作器快照。

接著將 orderId 給複製出來後,讓我們來確認一下資料庫的資料。

Production Service history 資料表

依照我們的業務流程,Production Service 的 history 資料表顯示了庫存已扣除成功的相關記錄。

Order Service order 資料表

再看到 Order Service 的 order 資料表,相關的訂單資訊也被建立成功。我們緊接著來到最後一個微服務:

User Service history 資料表

因為在最後一個步驟中我們於匿名函式裡埋藏了一個 exit ,所以我們看不到 User Service 有任何扣款記錄的存在。

建立一個 {Anser-Tutorial-Service}/rebuild_restarter.php 檔案,並鍵入以下內容:

<?php

require_once './init.php';
use SDPMlab\Anser\Orchestration\Saga\Restarter\Restarter;
use Orchestrators\CreateOrderOrchestrator;

$restarter = new Restarter();
$result = $restarter->reStartOrchestratorsByServer(
    className: CreateOrderOrchestrator::class,
    serverName: 'AnserTutorialService',
    reBuild: true
);

var_dump($result);

上述的 PHP 程式與上一章所實作的 compensation_restarter.php 大同小異,唯一的不同是我們加入了 bool $reBuild 參數並將他設定為 true

在開始執行這個 PHP 程式之前,先記得前往包含錯誤的協作器中,將失敗的罪魁禍首 exit 給註解掉:

 //Step4 使用者錢包扣款
$this->setStep()->setCompensationMethod('rollbackUserWalletCharge')
    ->addAction(
        alias: 'walletCharge',
        action: static function (CreateOrderOrchestrator $runtimeOrch) {
            //exit;
            $userKey = $runtimeOrch->getStepAction('userInfo')->getMeaningData()['data']['u_key'];
            $total = $runtimeOrch->getStepAction('createOrder')->getMeaningData()['total'];
            return $runtimeOrch->userService->walletChargeAction($userKey, $runtimeOrch->orderId, $total);
        }
    );

最後你就可以直接使用 php rebuild_restarter.php 啟動上述 PHP 程式。順利的話你應該能看到下列內容出現在你的 Command Line 介面:

此時,讓我們去到 User Service 的 history 資料表重新查詢,你應該能看見最後一個步驟被正確完成了!

重新執行成功的 User Service history 資料表

再去到 Production Service 的 history 資料表看看,你會發現一些有趣的事情:

經歷補償再重新執行的 Production Service history 資料表

在 Anser Restarter 中,並不存在協作器的「斷點執行」。我們會先透過「補償」行為讓微服務的資料回到最初尚未執行協作器的資料一致狀態,再對協作器進行重試,從步驟 0 重新開始,因此你才會在上述資料表中看見相同的 o_key 經歷了建立、補償、記錄軟性刪除,最後再重新建立的過程。

不分區分 serverName 地重新執行

如果你的協作器使用多個伺服器同時提供服務,你也可以直接透過下列程式碼跳過 serverName 的過濾,對所有符合 className 的協作器進行重新執行:

$restarter = new Restarter();
$result = $restarter->reStartOrchestratorsByClass(
    className: CreateOrderOrchestrator::class,
    reBuild: true
);

結語

透過 Anser-Saga 的高可用性機制,我們在面對協作器中斷時,擁有了兩個有力的手段來保全資料的一致性和業務流程的完整性,分別是「補償被中斷的協作器快照」和「重新執行被中斷的協作器快照」。

  • 補償被中斷的協作器快照:
    補償的核心是撤回已完成的操作,使系統回到最初的狀態。這種方法適用於當我們發現協作器的某些操作出現錯誤,且無法繼續完成後續操作時。補償可以保證系統的資料一致性。
  • 重新執行被中斷的協作器快照:
    重新執行的目的是在解决協作器中斷的原因後,從頭開始重新執行協作器的所有操作。這種方法特別適合於錯誤狀況相對簡單或已明確知道錯誤原因的情況下。重新執行可以節省資源,特別是當中斷點接近協作器完成時。然而,值得注意的是,在重新執行前必須保證已修復引起中斷的錯誤,否則可能會再次遭遇相同的問題。

「補償」和「重新執行」各有利弊,選擇哪種方法取決於實際的錯誤情境和業務需求。如果錯誤較為複雜,或是尚未明確找到錯誤原因,那麼「補償」可能是更為安全的選擇;反之,如果錯誤原因明確,且已經得到修復,那麼「重新執行」可能是更為有效和節省資源的選擇。

在實際的情境中,由使用者建立起的訂單或資料若直接進行補償,可能會造成商業行為上的風險,若我們能排除錯誤於補償後自動重試協作器,那麼將獲得更好的使用者體驗;又或者某些錯誤的協作器執行讓使用者被扣除多餘的款項,或者是商品庫存、折價券損失等需要回朔的協作器錯誤,就很適合直接進行補償,使系統回到最初的狀態。

透過合理地選擇補償或重新執行的策略,我們可以更靈活地應對協作器中斷的問題,提高系統的可靠性和健壯性。


上一篇
第二十三章、Anser-Saga:補償被中斷的協作器快照 - PHP 微服務入門與開發
下一篇
第二十五章、Anser-Saga:深入執行週期的高可用性元件 - PHP 微服務入門與開發
系列文
30 天上手! PHP 微服務入門與開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言