在使用 GRPC 前我們要先稍微了解一下什麼是 GRPC,GRPC 是由 Google 所開發出來基於 Http2.0 的一種架構,主打著跨平台、高效能。
在使用 GRPC 前也須先學習 Protocol Buffer 語法,他是 GRPC 的資料交換格式。
首先先來定義一個 Protocol Buffer
syntax = "proto3";package protobuf.user;message User { // 包含哪些欄位資訊
uint32 id = 1;}message UserRequest { // 參數
}service UserService {
rpc GetUser(UserReqeust) returns(User) {};}
syntax: 定義 protocol buffer 所使用的版本, 上面範例是設定為使用 protocol buffer 3, 沒有設定的話預設為使用 2 版。
package: 可以避免 service 以及 message 名稱衝突。
message: 定義訊息結過需使用,以上範例為定義一個 User 的訊息結構,而裡面則定義了這個訊息結構所包含的資訊有哪些。
service: 定義 grpc 服務 (講白一點就是定義這個 GRPC 有哪些 api 可以使用)。
rpc … returns : 定義 grpc 服務可以使用的 method 以及回傳值為何,以上面的例子來看的話就是有一個 GetUser 的 method, 而參數為 UserRequest 回傳值為 User。
注意!!! 假如使用 oneof 的話則型別不能是 repeated 以及 map
想要更加深入學習 protocol buffer 的話可以到官網閱讀文件 https://developers.google.com/protocol-buffers/docs/proto3#services
以下稍後直接說明如何在本機進行 GRPC 的開發
注意!!!! PHP GRPC 目前只支援 unidirectional API
首先要先安裝 php grpc extension
安裝 grpc extension
sudo pecl install grpc
安裝完成後可以查看 php.ini 是否有加入了 grpc extension
extension="grpc.so"
安裝 protobuf
sudo pecl install protobuf
一樣安裝完成後可以查看 php.ini 是否已加入 protobuf extension
extension="protobuf.so"
這邊還要再安裝產生 grpc client code 套件以及 grpc server code 套件
ps: 以下範例用 mac 當作教學
首先安裝產生 client code 的 command, 而 mac 可以直接透過 homebrew 安裝
brew install grpc
安裝完成後再 /usr/local/bin 應該可以看到 grpc-php-plugin 這個檔案
ls -al /usr/local/bin | grep grpc
緊接著要安裝產生 server code 的 binary 檔案, 這邊我是直接使用 go 進行安裝(假如沒有 安裝 go 的人可以先去安裝 go)
go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
安裝完成後就會在 go path 的 bin 資料夾下看到該檔案, 我這邊 go path 設定的位置是加入下的 go 資料夾中
ls -al /<username>/go/bin
接著在 laravel Server & Client 專案安裝 grpc 套件
composer 要安裝以下幾個套件
composer require google/protobuf
composer require grpc/grpc
然後假如是要作為 grpc server 的話要再多安裝
composer require spiral/roadrunner-grpc
所需工具都安裝完成後我們緊接著進行簡單的 proto 產生
首先在專案中新增 protobuf/src/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;
}
}
透過以下指令分別產生 server & client code
產生 server code (要透過 protoc-gen-php-grpc)
protoc --proto_path=protobuf \ # proto 位置
--php_out=protobuf/build \ # 資料要產生在何處
--grpc_out=./protobuf/build \ # server 的 code 要產生到何處
--plugin=protoc-gen-grpc=/<home>/go/bin/protoc-gen-php-grpc \
./protobuf/src/user.proto # protobuf 檔案
該指令會產生在 protobuf 資料夾下
緊接著產生 client code, 而 client code 的部分要透過 grpc_php_plugin
protoc --proto_path=protobuf --php_out=protobuf/build --grpc_out=./protobuf/build --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin ./protobuf/src/greet.proto
下完指令後會產生一個 UserServiceClient.php 的檔案
視情況要在 composer.json 的 autoload 增加類似以下的 的設定, 設定完成後重新執行 composer dump-autoload
透過 composer 安裝的 spiral/roadrunner-grpc 去下載 rr binary
./vendor/bin/rr get-binary
安裝完成後會在專案根目錄下看到 rr binary 檔案
接著在 server grpc 專案根目錄下新增 worker.php
<?php
use Illuminate\Contracts\Console\Kernel;
require_once __DIR__ . '/vendor/autoload.php';
/** 載入 Laravel 核心 */
$app = require_once __DIR__.'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
/** 加入 gRPC Server 物件 */
$server = $app->make(\Spiral\GRPC\Server::class);
/** 註冊想要的服務 */
$server->registerService(\Mypackage\UserServiceInterface::class, new \App\Services\UserService());
/** 啟始 worker */
$worker = new Spiral\RoadRunner\Worker(new Spiral\Goridge\StreamRelay(STDIN, STDOUT));
$server->serve($worker);
以及設定 .rr.yaml 設定檔案
version: "2.7"
grpc:
listen: "tcp://0.0.0.0:50051"
proto: "protobuf/src/user.proto"
workers:
command: "php worker.php"
pool:
numWorkers: 4
設定完後接著啟動 grpc server
./rr serve -c ./.rr.yaml -d
grpc server 啟動起來後在 console 應該會看到類似的畫面, 看到類似的畫面及代表成功啟動。
然後這邊假如 server 有改 code 的話你會發現 client 端打 server 還是會是舊的 code, 會需要將 grpc server 再重新啟動 server 才會使用最新的程式碼。為了處理每次都要手動重啟的麻煩,我們這邊將進行額外的設定去進行 hot reload,設定如下
我們在 server 端的 .rr.yaml 設定
# hot reload
reload:
# 間隔幾秒更新
interval: 1s
# 全域的檔案格式
patterns:
- .php
# 哪些服務要 reload
services:
grpc:
# recursive 搜尋檔案格式
recursive: true
# 忽略哪些資料夾
ignore:
- vendor
patterns:
- .php
dirs:
- .
設定好後重新啟動 grpc server, 之後假如 server 端有更改程式碼的話, grpc 都會重新 reload。
想了解夠多 roadrunner plugin 裡面的設定可以到官網進行學習 https://roadrunner.dev/docs/plugins-reload/2.x/en
而 GRPC 有分為四種類型
- Unary: 單向,Client 發送一次 Request, Server Response 一次
- Server Streaming: Client 發送一次 Request, Server 可以多次回傳 Response
- Client Streaming: Client 發送多次 Request, Server Response 一次
- Bi Directional Streaming: 雙向多次,也就是 Client 發送多次 Request, Server 也會回傳多次 Response
但是注意!!! 目前 PHP 只有 Unary 可以使用,假如想要使用 Server Streaming, Client Streaming 等可以使用 Golang
參考資料:
- https://github.com/spiral/php-grpc
- https://hackmd.io/@tML6ejGhR7q68VfQ4kLDQg/By-WdVz_Y
- https://zhuanlan.zhihu.com/p/109428382
- https://poyu677.medium.com/grpc-%E4%BD%BF%E7%94%A8-php-%E5%AF%A6%E4%BD%9C-ab485e9f1044
- https://pkg.go.dev/lab.yaozh.com/kz-rr/php-grpc/cmd/protoc-gen-php-grpc
6. https://www.zybuluo.com/phper/note/1764719
7. https://doc.oschina.net/grpc?t=60136
8. https://grpc.io/docs/languages/php/basics/
9. https://roadrunner.dev/docs/intro-about/2.x/en
11. https://github.com/spiral/roadrunner-laravel
12. https://ithelp.ithome.com.tw/articles/10245345
13. https://dev.to/khepin/building-a-grpc-server-in-php-3bgc