hyperf/docs/zh-cn/grpc.md
2020-09-18 14:44:09 +08:00

195 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# gRPC 服务
gRPC 官方文档中的 quickstart - php, 很容易给 PHPer 产生误导, 按照官网的文档, 运行起来 gRPC 服务就很麻烦, 更不用说整套的 RPC 服务了.
推荐阅读 [tech| 再探 grpc](https://www.jianshu.com/p/f3221df39e6f), 讲解了在 PHP 中实现 gRPC 相关基础知识.
hyperf 对 gRPC 支持做了更进一步的封装, hyperf-skeleton 项目为例, 详细讲解整个步骤:
- .proto 文件以及相关配置实例
- gRPC server 示例
- gRPC client 示例
## .proto 文件以及相关配置实例
- 定义好 proto 文件 `grpc.proto`
```proto3
syntax = "proto3";
package grpc;
service hi {
rpc sayHello (HiUser) returns (HiReply) {
}
}
message HiUser {
string name = 1;
int32 sex = 2;
}
message HiReply {
string message = 1;
HiUser user = 2;
}
```
- 使用 protoc 生成示例代码
```
# 使用 linux 包管理工具安装 protoc, 下面以 alpine 为例, 也可以参考 hyperf-skeleton 下的 Dockerfile
apk add protobuf
# 使用 protoc 自动生成代码
protoc --php_out=grpc/ grpc.proto
# tree grpc
grpc
├── GPBMetadata
│   └── Grpc.php
└── Grpc
├── HiReply.php
└── HiUser.php
```
- 配置 composer.json, 使用 `grpc/` 下代码的自动加载. 如果 proto 文件中使用不同的 `package` 设置, 或者使用了不同的目录, 进行相应调整即可,添加之后执行 `composer dump-autoload` 使自动加载生效
```json
"autoload": {
"psr-4": {
"App\\": "app/",
"GPBMetadata\\": "grpc/GPBMetadata",
"Grpc\\": "grpc/Grpc"
},
"files": [
]
},
```
## gRPC server 示例
- gRPC server 服务器配置
`server.php` 文件(参考 [配置](zh-cn/config.md)):
```php
'servers' => [
....
[
'name' => 'grpc',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9503,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
SwooleEvent::ON_REQUEST => [\Hyperf\GrpcServer\Server::class, 'onRequest'],
],
],
],
```
- gRPC server 路由配置
`routes.php` 文件(参考 [路由](zh-cn/router.md)):
```php
Router::addServer('grpc', function () {
Router::addGroup('/grpc.hi', function () {
Router::post('/sayHello', 'App\Controller\HiController@sayHello');
});
});
```
`HiController.php` 文件中的 `sayHello` 方法:
```php
public function sayHello(HiUser $user)
{
$message = new HiReply();
$message->setMessage("Hello World");
$message->setUser($user);
return $message;
}
```
.proto 文件中的定义和 gRPC server 路由的对应关系: `/{package}.{service}/{rpc}`
- 如果想更深入一点
gRPC server 如何对 gRPC 请求进行处理的: `\Hyperf\GrpcServer\CoreMiddleware::process()` (`vendor/hyperf/grpc-server/src/CoreMiddleware.php:46`, 复制后直接使用 phpstorm 打开), 解析出 `request_uri`, 即得到 `/{package}.{service}/{rpc}` 信息, 然后调用好封装好的 gRPC 编解码类 `\Hyperf\Grpc\Parser::deserializeMessage`(`vendor/hyperf/grpc-server/src/CoreMiddleware.php:137`), 就可以获取到请求的明文信息
gRPC server 如何进行 gRPC 响应, 相信你可以根据上面的信息, 自己发现.
## gRPC client 示例
示例代码可以在 `GrpcController` 中找到:
```php
public function hello()
{
// 这个client是协程安全的可以复用
$client = new \App\Grpc\HiClient('127.0.0.1:9503', [
'credentials' => null,
]);
$request = new \Grpc\HiUser();
$request->setName('hyperf');
$request->setSex(1);
/**
* @var \Grpc\HiReply $reply
*/
list($reply, $status) = $client->sayHello($request);
$message = $reply->getMessage();
$user = $reply->getUser();
var_dump(memory_get_usage(true));
return $message;
}
```
hyperf 已经封装好了 `\Hyperf\GrpcClient\BaseClient`, 只要根据 .proto 文件中的定义, 按需扩展:
```php
class HiClient extends BaseClient
{
public function sayHello(HiUser $argument)
{
return $this->_simpleRequest(
'/grpc.hi/sayHello',
$argument,
[HiReply::class, 'decode']
);
}
}
```
gRPC客户端还支持 gRPC 的 Streaming 模式。以双向流为例:
```php
<?
public function hello()
{
$client = new RouteGuideClient('127.0.0.1:50051');
$note = new RouteNote();
$call = $client->routeChat();
$call->push($note);
$call->push($note);
/** @var RouteNote $note */
[$note,] = $call->recv();
[$note,] = $call->recv();
}
```
> 请注意在 streaming 模式下,您必须手动捕获连接断开的异常 (`Hyperf\GrpcClient\Exception\GrpcClientException`) 并根据需要选择是否重试。
## 写在后面
如果你是 gRPC 的重度使用者, 欢迎关注 hyperf 的后续开发者工具, 可以根据 .proto 文件生成全套 gRPC 代码.