iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
Modern Web

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

第十二章、Anser-Orchestration:處理服務協作邏輯 - PHP 微服務入門與開發

  • 分享至 

  • xImage
  •  

從這個章節開始,我們將會使用到第四章中除了 Main App 以外的所有服務。因此,尚未建立相關環境的讀者請前往第四章並依照教學建立起你的本地環境;除此之外,我們將使用 Anser-Tutorial-Service 作為我們的示範環境。請至 GitHub Releases 中下載所需的檔案。

將上述檔案解壓縮至本地環境後,你應該能看到一個由 FiltersLogsOrchestratorsServices 等資料夾組成的專案。這個專案結構即是 Anser 所推薦的組織模式,所有專案的檔案皆遵循 PSR-4 自動載入標準中關於檔案名稱與資料夾命名的規範。

使用 Command Line 介面定位至 Anser-Tutorial-Service 目錄下,以 docker compose up -d 啟動環境。這裡需要注意的是,這個開發環境與第四章的 Main App 使用同一個外部 port 8083,若你是一路依序實作過來的讀者,則必須先關閉本地的 Main App。

接著,使用 composer install 將專案所需的依賴檔案給下載回來,此時你的資料夾應該會長的像是這個樣子:

未來所有的程式進入點(Postman 存取的 PHP 檔案)都需要 require_once init.php ,若你仔細觀察已經存在的檔案,你應該能夠發現他們的 require_once 都被拿掉了。這是因為我們採用了 composer 的 psr-4 autoload 功能,你可以參考根目錄下的 composer.json

因為第六行到第九行的宣告,使我們的 composer 在執行 composer updatecomposer install 時會自動掃描符合 psr-4 規範的資料夾,再將它們處理成自動載入的腳本,這樣我們就能免除在檔案中加上 require_once 的不便了。若未來讀者需要自行建立資料夾或檔案,可以直接執行 composer dump-autoload ,這個指令將會重新掃描資料夾與其他規則,再重新產生 autoload 所需的資料。

最後,因為 Anser-Tutorial-Service 專案已經實作了所有第四章提到的所有 Service Endpoints,所以你的本地環境必須同時擁有 Production ServiceUser Sercie 以及 OrderService

撰寫服務協作

你應該在 {project_root}/Orchestrators 中發現上週提到的 UserLoginOrchestrator ,本周會詳細的介紹 Anser 的服務協作邏輯與協作器的使用方式。

<?php
namespace Orchestrators;

use SDPMlab\Anser\Orchestration\Orchestrator;
use Services\UserService;
class UserLoginOrchestrator extends Orchestrator
{
    protected $userService;

    public function __construct()
    {
        $this->userService = new UserService();
    }

    protected function definition(string $email = "", string $password = "")
    {
        $this->setStep()
            ->addAction('login', $this->userService->userLoginAction($email, $password));
        $this->setStep()
            ->addAction('info', static function(UserLoginOrchestrator $runtimeOrch){
                $data = $runtimeOrch->getStepAction('login')->getMeaningData();
                return $runtimeOrch->userService->userInfoAction($data['token']);
            });
        $this->setStep()
            ->addAction('wallet', static function(UserLoginOrchestrator $runtimeOrch){
                $data = $runtimeOrch->getStepAction('info')->getMeaningData();
                return $runtimeOrch->userService->walletAction($data['data']['u_key']);
            });
    }

    protected function defineResult(): array
    {        
        $data = [
            "token" => $this->getStepAction('login')->getMeaningData()['token'],
            "userData" => $this->getStepAction('info')->getMeaningData()['data'],
            "walletInfo" => $this->getStepAction('wallet')->getMeaningData()['data']
        ];
        return $data;
    }

}

在上述的範例協作器中,我們首先是在類別的建構子中初始化了 UserSercie 類別,這個類別在第九章的服務抽象化有清楚的解釋。透過抽象化的服務類別,就可以在協作器中以明確語意化的方式取得需要被存取的準確服務端點。

指揮微服務的協作邏輯

所有繼承 SDPMlab\Anser\Orchestration\Orchestrator 的協作器,都需要覆寫 definition() 方法來定義協作器的執行細節。值得注意的是,因為父類別的 definition() 方法為一個保護方法 protected function definition(); ,因此在定義這個方法所需的傳入參數時,你必須明確地給予參數們初始值,就像範例那樣:

protected function definition(string $email = "", string $password = "")

接著,你可以透過 setStep() 方法,開始撰寫你的協作器編排邏輯。協作邏輯的實作基礎是建立一系列的步驟(Step)。每個步驟可以代表一個甚至是複數的微服務操作或請求,在 Anser 中我們稱這種溝通為行動(Action)。透過setStep()方法,你可以建立新的步驟並透過鏈式操作(Chain Operation)加入該步驟需要的行動(Action)。

例如,在 UserLoginOrchestrator 中,我們建立了三個步驟:

第一步驟:進行用戶登入,其行動名稱為 login
第二步驟:取得用戶資訊,其行動名稱為 info
第三步驟:取得用戶的錢包資訊,其行動名稱為 wallet

步驟與行動

呼叫 setStep() 後,協作器將會自動宣告一個空的 Step 類別實體,並將它回傳給你。接著,每個步驟都可以透過 addAction()方法添加操作。該方法接受兩個參數:

  • string $alias 本次行動的別名
  • Closure|ActionInterface $action 與行動的內容

$action 可以是一個已經實體化的 Action 類別(例如:UserService 直接回傳的 Action),或是一個可呼叫的匿名函數。

若你選擇直接傳入 Action 實體至 addAction(),那麼協作器將會自動幫你掌管這個 Action 的生命週期,在輪到它的時候對微服務展開連線。

若是你選擇傳入匿名函數,那麼你將可以獲得執行時期的協作器,你可以在這個函式中進行更多的邏輯操作或動態地根據前面步驟的結果來呼叫微服務。這提供了開發人員高度的彈性,允許協作器在執行時進行動態決策。比如說:你可能需要根據第一步驟的結果,來決定第三步驟具體要呼叫哪個微服務。

依據在上述的 info 步驟中,我們使用了一個匿名函數。這接著從 login 步驟中取得的資料來決定怎麼取得用戶的資訊。

->addAction('info', static function(UserLoginOrchestrator $runtimeOrch){
    $data = $runtimeOrch->getStepAction('login')->getMeaningData();
    return $runtimeOrch->userService->userInfoAction($data['token']);
});

透過在 addAction() 中傳入匿名函數,藉由取得執行時期當下的協作器 $runtimeOrch,就可以使用它來取得先前步驟的結果。這邊我們使用 getStepAction(string $alias) 來取得上一個步驟的的 login Action。

你需要特別注意的是,在你完成了所有的邏輯判斷後,你必須在這個匿名函數中回傳一個 Action 實體。

總結

透過上述的解說,我們深入地了解了如何使用 Anser 來指揮簡單的微服務協作。協作器是指揮微服務互動的關鍵部分。透過定義各個步驟和行動,你可以精確地控制每個微服務應該在何時被呼叫。

每個步驟可以包含一個或多個行動,這些行動可以是靜態的(直接呼叫一個服務)或動態的(基於先前步驟的結果來決定的行動);透過使用匿名函數,開發人員可以根據先前步驟的結果進行動態的邏輯決策。這提供了非常大的彈性,允許協作器根據執行時的實際情況進行調整。

微服務的編排並不是一件簡單的事,但有了 Anser 這樣的工具,開發者可以更有效地建立、測試和維護複雜的微服務互動。


上一篇
第十一章、Anser-Orchestration:簡單的服務協作 - PHP 微服務入門與開發
下一篇
第十三章、Anser-Orchestration:處理服務協作的回傳 - PHP 微服務入門與開發
系列文
30 天上手! PHP 微服務入門與開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言