上篇分享我們簡單介紹了 Thrift 框架,本周學院君將會花幾個篇幅的教程來介紹如何基于 Thrift + Laravel 構建微服務接口。
項目初始化
為此,我們先初始化一個新的 Laravel 應用 thrift
:
laravel new thrift
在 thrift
項目根目錄下新增一個 thrift
子目錄,然后在該子目錄下創建 Thrift IDL 文件 user.thrift
,用于定義和用戶相關的服務接口(語言為 PHP,命名空間為 App\Thrift\User
):
namespace php App.Thrift.User
// 定義用戶接口
service User {
string getInfo(1:i32 id)
}
接著在項目根目錄下運行如下命令,根據上述 IDL 文件生成相關的服務代碼:
thrift -r --gen php:server -out ./ thrift/user.thrift
這樣就會在 App\Thrift\User
命名空間下生成對應的服務代碼:
然后通過 Composer 安裝 Thrift PHP 依賴包:
composer require apache/thrift
編寫服務端代碼
接下來,我們就可以編寫服務端代碼了,在 app
目錄下新建一個 Services/Server
子目錄,然后在該目錄下創建服務接口類 UserService
,該類實現自 App\Thrift\User\UserIf
接口:
在服務接口實現中,我們通過傳入參數查詢數據庫并返回對應的記錄,這里為了簡化邏輯,我們直接調用模型類查詢記錄并直接返回,將參數校驗、緩存優化、異常處理通通省略。
接下來,我們來編寫服務端啟動命令類,在 Laravel 框架中,這可以通過 Artisan 控制臺來完成,首先創建命令類:
php artisan make:command RpcServerStart
該命令會在 app/Console/Commands
目錄下生成 RpcServerStart.php
,我們編寫 RpcServerStart
命令類代碼如下:
<?php
namespace App\Console\Commands;
use App\Services\Server\UserService;
use App\Thrift\User\UserProcessor;
use Illuminate\Console\Command;
use Thrift\Exception\TException;
use Thrift\Factory\TBinaryProtocolFactory;
use Thrift\Factory\TTransportFactory;
use Thrift\Server\TServerSocket;
use Thrift\Server\TSimpleServer;
use Thrift\TMultiplexedProcessor;
class RpcServerStart extends Command
{
/**
* The name and signature of the console command.</pre>
*
* @var string
*/
protected $signature = 'rpc:start';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Start Thrift RPC Server';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
try {
$thriftProcess = new UserProcessor(new UserService());
$tFactory = new TTransportFactory();
$pFactory = new TBinaryProtocolFactory();
$processor = new TMultiplexedProcessor();
// 注冊服務
$processor->registerProcessor('UserService', $thriftProcess);
// 監聽本地 8888 端口,等待客戶端連接請求
$transport = new TServerSocket('127.0.0.1', 8888);
$server = new TSimpleServer($processor, $transport, $tFactory, $tFactory, $pFactory, $pFactory);
$this->info("服務啟動成功[127.0.0.1:8888]!");
$server->serve();
} catch (TException $exception) {
$this->error("服務啟動失敗!");
}
}
}
別忘了在 app/Console/Kernel.php
中注冊上述命令類使其生效:
use App\Console\Commands\RpcServerStart;
protected $commands = [
RpcServerStart::class,
];
這樣,服務端接口和啟動命令都已經完成了,接下來我們繼續編寫客戶端建立連接和請求通信代碼。
編寫客戶端代碼
這個客戶端并不是前端、移動端,而是相對于 RPC 服務器的 RPC 客戶端,我們在 app/Services/Client
目錄下創建 UserService.php
,用于存放 RPC 客戶端連接與請求服務接口方法:
<?php
namespace App\Services\Client;
use App\Thrift\User\UserClient;
use Thrift\Protocol\TMultiplexedProtocol;
use Thrift\Exception\TException;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TBufferedTransport;
use Thrift\Transport\TSocket;
class UserService
{
public function getUserInfo(int $id)
{
try {
// 建立與 RpcServer 的連接
$socket = new TSocket("127.0.0.1", "8888");
$socket->setRecvTimeout(30000); // 超時時間
$socket->setDebug(true);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$thriftProtocol = new TMultiplexedProtocol($protocol, 'UserService');
$client = new UserClient($thriftProtocol);
$transport->open();
$result = $client->getInfo($id);
$transport->close();
return $result;
} catch (TException $TException) {
dd($TException);
}
}
}
同樣,為了簡化代碼和流程,我這里將連接和請求代碼寫到一起了,如果有多個服務接口,傳輸層是可以共用的,需要拆分開。這里我們先建立與 RPC 服務器的連接,然后通過調用 App\Thrift\User\UserClient
提供的 getInfo
請求 RPC 服務端 App\Services\Server\UserService
類提供的 getInfo
方法獲取用戶信息并返回。
最后,我們在 routes/web.php
中注冊客戶端請求路由:
use App\Services\Client\UserService;
Route::get('/user/{id}', function($id) {
$userService = new UserService();
$user = $userService->getUserInfo($id);
return $user;
});
該路由通過匿名函數定義了路由處理邏輯:調用 App\Services\Client\UserService
的 getUserInfo
方法獲取用戶信息。需要與傳統的 PHP 請求處理區分的是,這個請求底層不是在本地通過內存調用完成的,而是通過 RPC 客戶端請求 RPC 服務端接口基于遠程服務調用完成的。
測試 RPC 服務調用
至此,RPC 客戶端和服務端代碼都已經編寫好了,接下來我們來測試這個 RPC 接口調用。
修改項目根目錄下 .env
中的數據庫相關配置,運行數據遷移生成 users
表:
php artisan migrate
然后初始化 users
表數據:
接下來,在項目根目錄下啟動 Thrift RPC 服務端:
php artisan rpc:start
再新開一個終端窗口啟動 Laravel 應用(RPC 客戶端):
php artisan serve
然后就可以在瀏覽器中訪問獲取用戶信息的路由:
成功獲取到用戶記錄,說明 RPC 遠程服務調用成功。
可以看到,Thrift 只是個 RPC 框架而已,只解決了通信協議和數據傳輸問題,對于服務治理和容錯并沒有提供對應的解決方案,需要我們自己去實現,而且基于 PHP 實現的服務器在高并發情況下可能無法滿足微服務接口的性能要求,后面我們將從這幾個方面來完善這個示例應用。