ProtoBuf on Laravel
ProtoBuf on Laravel
實作 proto 建置, gRPC Server 及 gRPC Client。 以下內容是假定在 Laravel 已運行良好情況下開始設置。
實作環境
- OS: Ubunto 20.04
- PHP: 8.04
- Laravel: 8
運行環境需要的指令及延伸套件
- Install PHP extension for protobuf
$ sudo pecl install protobuf
- Install protoc command
$ sudo apt install protobuf-compiler
- Instal protobuf complier grpc
$ sudo apt install protobuf-compiler-grpc
Laravel
可以新建一個 Laravel 專案來測試,或是在原本已有的 Larvel 專案。
- Install google/protobuf to Laravel
$ cd {laravel-project-folder} $ composer require google/protobuf
- create protobuf folder structure to base path
$ cd {laravel-project} $ mkdir protobuf $ mkdir protobuf/build $ mkdir protobuf/src
-
make *.proto file to protobuf/src
example: user.proto
syntax = "proto3"; package mypackage; message UserRequest { uint32 id=1; } message User { uint32 id = 1; string name = 2; string email = 3; string created_at = 4; string updated_at = 5; }
- generate protobuf program for php
$ protoc --proto_path=protobuf --php_out=protobuf/build protobuf/src/user.proto
這個指令會自動幫你生成 protobuf 需要的 php 檔在protobuf/build 裡,這些檔,我們都不用去動它。
- set psr-4 autoload for php in composer.json
"autoload": { "psr-4": { "App\": "app/", "Database\Factories\": "database/factories/", "Database\Seeders\": "database/seeders/", "Mypackage\": "protobuf/build/Mypackage/", "": "protobuf/build/" } },
到這裡,你可以試著用 protoc 產生的物件來試著轉換自己的 Model 資料。
gRPC on Laravel
Install grpc related packages
$ composer require grpc/grpc
$ composer require spiral/roadrunner-grpc
Download rr-grpc and protoc-gen-php-grpc command
Go to https://github.com/spiral/php-grpc/releases to downd latest version
- save rr-grpc to laravel base folder
$ cd {laravel-project-folder} $ cp {download-folder}/{extrac-folder}/rr-grpc ./
- save protoc-gen-php-grpc to /usr/bin
$ sudo cp {download-folder}/{extrac-folder}/protoc-gen-php-grpc /usr/bin
Generator protobuf files
參考上面Laravel的步驟,但這裡要加入 Server 的設定
Example
user.proto
syntax = "proto3";
package mypackage;
service UserService {
rpc getUser(UserRequest) returns(User) {}
}
message UserRequest {
uint32 id=1;
}
enum Gender {
male = 0;
female = 1;
other = 2;
}
message User {
uint32 id = 1;
string name = 2;
string email = 3;
string created_at = 4;
string updated_at = 5;
message UserProfile {
string nickname = 1;
string intro = 3;
Gender gender = 4;
string birthday = 5;
}
}
這個範例中加入了 rpc server 相關的設置
service UserService {
rpc getUser(UserRequest) returns(User) {}
}
這個在我們產生相關檔案時,會自動幫我們建立 UserService 的 Interfeace, 裡面則有一個 getUser 的 Method 會傳回 User 這個 message 物件。
Generate protoBuf php files
$ protoc --proto_path=protobuf --php_out=protobuf/build --grpc_out=protobuf/build --plugin=protoc-gen-grpc=/usr/bin/protoc-gen-php-grpc protobuf/src/user.proto
Usage: protoc [OPTION] PROTO_FILES
OPTIONS
--proto_path: proto 檔案的所在資料夾
--php_out: php 檔產生處
--grpc_out: 用 protoc-gen-php-grpc 產生的檔案存放位置
--plugin: 使用 plugin, 這裡指定用 protoc-gen-grpc 這個 plugin, 後面再指定指令所在位置
實作 UserService
這裡就依自己在 Laravel 中習慣去建立你自己的物作。
建立 App/Serivces/UserService (app/Services/UserService.php)
<?php
namespace AppServices;
use AppModelsUser as DBUser;
use MypackageUser;
use MypackageUserRequest;
use MypackageUserServiceInterface;
use SpiralGRPC;
class UserService implements UserServiceInterface
{
public function getUser(GRPCContextInterface $ctx, UserRequest $in): User
{
$userId = $in->getId();
$user = DBUser::find($userId);
$userProto = new User();
$userProto->setId($user->id);
$userProto->setName($user->name);
$userProto->setEmail($user->email);
$userProto->setCreatedAt((string) $user->created_at);
$userProto->setUpdatedAt((string) $user->updated_at);
return $userProto;
}
}
建立 gRPC 執行的 worker
Create worker.php in base path on laravel
<?php
use IlluminateContractsConsoleKernel;
require_once __DIR__ . '/vendor/autoload.php';
/** 載入 Laravel 核心 */
$app = require_once __DIR__.'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
/** 加入 gRPC Server 物件 */
$server = $app->make(SpiralGRPCServer::class);
/** 註冊想要的服務 */
$server->registerService(MypackageUserServiceInterface::class, new AppServicesUserService());
/** 啟始 worker */
$worker = new SpiralRoadRunnerWorker(new SpiralGoridgeStreamRelay(STDIN, STDOUT));
$server->serve($worker);
設定 gRPC
gRPC 是由 rr-grpc 來啟動,它是由 rr 來改寫的,所以一樣吃 .rr.ymal 的設定檔
rr 是 roadrunner 的縮寫,指令即其縮寫。
.rr.yaml
grpc:
listen: "tcp://0.0.0.0:50051"
proto: "protobuf/src/user.proto"
workers:
command: "php worker.php"
pool:
numWorkers: 4
啟動 gRPC Server
$ ./rr-grpc serve -v -d
gRPC Server 就起來了,接下來設置 gRPC Client 來測試連線。
建立 gRPC Client for PHP
一樣由 protoc 來自動產生 php code
$ protoc --php_out=protobuf/build --grpc_out=protobuf/build --plugin=protoc-gen-grpc=/usr/bin/grpc_php_plugin protobuf/src/user.proto
--plugin: 使用 plugin, 這裡指定用 protoc-gen-grpc 這個 plugin, 改用 grpc_php_plugin 指令。
跟前面產生 UserServiceInterface.php 類似,但這裡產生 UserServiceClient.php
開始取得資料
建以一個 user.php 的指令
$ cd {laravel-project-folder}
$ touch user.php
$ chmod 755 user.php
user.php 內容
#!/usr/bin/php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use MypackageUserServiceClient;
use MypackageUserRequest;
use GrpcChannelCredentials;
$client = new UserServiceClient('0.0.0.0:50051', [
'credentials' => ChannelCredentials::createInsecure(),
]);
if (!isset($argv[1])) {
die("Please specify the user id
");
}
$userId = $argv[1];
$request = new UserRequest();
$request->setId($userId);
/** @var User $response */
list($response, $status) = $client->getUser($request)->wait();
if ($status->code !== GrpcSTATUS_OK) {
echo "ERROR: " . $status->code . ", " . $status->details . PHP_EOL;
exit(1);
}
print $response->serializeToJsonString()."
";
用指令去取得資料
$ ./user.php {userId}
exampe: command
$ ./user.php 22
result
{"id":22,"name":"酈樺美","email":"dexter87@example.org","createdAt":"2021-10-26 15:03:51","updatedAt":"2021-10-26 15:03:51"}
留言板程式太舊,markdown 支援不齊,好讀版 Go to https://hackmd.io/@tML6ejGhR7q68VfQ4kLDQg/By-WdVz_Y