gRPC 服务

gRPC 官方文档中的 quickstart - php, 很容易给 PHPer 产生误导, 按照官网的文档, 运行起来 gRPC 服务就很麻烦, 更不用说整套的 RPC 服务了.

推荐阅读 tech| 再探 grpc, 讲解了在 PHP 中实现 gRPC 相关基础知识.

hyperf 对 gRPC 支持做了更进一步的封装, hyperf-skeleton 项目为例, 详细讲解整个步骤:

  • .proto 文件以及相关配置实例
  • gRPC server 示例
  • gRPC client 示例

.proto 文件以及相关配置实例

  • 定义好 proto 文件 grpc.proto
  1. syntax = "proto3";
  2. package grpc;
  3. service hi {
  4. rpc sayHello (HiUser) returns (HiReply) {
  5. }
  6. }
  7. message HiUser {
  8. string name = 1;
  9. int32 sex = 2;
  10. }
  11. message HiReply {
  12. string message = 1;
  13. HiUser user = 2;
  14. }
  • 使用 protoc 生成示例代码
  1. # 使用 linux 包管理工具安装 protoc, 下面以 alpine 为例, 也可以参考 hyperf-skeleton 下的 Dockerfile
  2. apk add protobuf
  3. # 使用 protoc 自动生成代码
  4. protoc --php_out=grpc/ grpc.proto
  5. # tree grpc
  6. grpc
  7. ├── GPBMetadata
  8. └── Grpc.php
  9. └── Grpc
  10. ├── HiReply.php
  11. └── HiUser.php
  • 配置 composer.json, 使用 grpc/ 下代码的自动加载. 如果 proto 文件中使用不同的 package 设置, 或者使用了不同的目录, 进行相应调整即可,添加之后执行 composer dump-autoload 使自动加载生效
  1. "autoload": {
  2. "psr-4": {
  3. "App\\": "app/",
  4. "GPBMetadata\\": "grpc/GPBMetadata",
  5. "Grpc\\": "grpc/Grpc"
  6. },
  7. "files": [
  8. ]
  9. },

gRPC server 示例

  • 安装组件
  1. composer require hyperf/grpc-server
  • gRPC server 服务器配置

server.php 文件(参考 配置):

  1. 'servers' => [
  2. ....
  3. [
  4. 'name' => 'grpc',
  5. 'type' => Server::SERVER_HTTP,
  6. 'host' => '0.0.0.0',
  7. 'port' => 9503,
  8. 'sock_type' => SWOOLE_SOCK_TCP,
  9. 'callbacks' => [
  10. Event::ON_REQUEST => [\Hyperf\GrpcServer\Server::class, 'onRequest'],
  11. ],
  12. ],
  13. ],
  • gRPC server 路由配置

routes.php 文件(参考 路由):

  1. Router::addServer('grpc', function () {
  2. Router::addGroup('/grpc.hi', function () {
  3. Router::post('/sayHello', 'App\Controller\HiController@sayHello');
  4. });
  5. });

HiController.php 文件中的 sayHello 方法:

  1. public function sayHello(HiUser $user)
  2. {
  3. $message = new HiReply();
  4. $message->setMessage("Hello World");
  5. $message->setUser($user);
  6. return $message;
  7. }

.proto 文件中的定义和 gRPC server 路由的对应关系: /{package}.{service}/{rpc}

  • 如果想更深入一点

gRPC server 如何对 gRPC 请求进行处理的(vendor/hyperf/grpc-server/src/CoreMiddleware.php): \Hyperf\GrpcServer\CoreMiddleware::process() 解析出 request_uri, 即得到 /{package}.{service}/{rpc} 信息, 然后调用封装好的 gRPC 编解码类 \Hyperf\Grpc\Parser::deserializeMessage, 就可以获取到请求的明文信息

gRPC server 如何进行 gRPC 响应, 相信你可以根据上面的信息, 自己发现.

gRPC client 示例

安装组件

  1. composer require hyperf/grpc-client

示例代码可以在 GrpcController 中找到:

  1. public function hello()
  2. {
  3. // 这个client是协程安全的,可以复用
  4. $client = new \App\Grpc\HiClient('127.0.0.1:9503', [
  5. 'credentials' => null,
  6. ]);
  7. $request = new \Grpc\HiUser();
  8. $request->setName('hyperf');
  9. $request->setSex(1);
  10. /**
  11. * @var \Grpc\HiReply $reply
  12. */
  13. list($reply, $status) = $client->sayHello($request);
  14. $message = $reply->getMessage();
  15. $user = $reply->getUser();
  16. var_dump(memory_get_usage(true));
  17. return $message;
  18. }

hyperf 已经封装好了 \Hyperf\GrpcClient\BaseClient, 只要根据 .proto 文件中的定义, 按需扩展:

  1. class HiClient extends BaseClient
  2. {
  3. public function sayHello(HiUser $argument)
  4. {
  5. return $this->_simpleRequest(
  6. '/grpc.hi/sayHello',
  7. $argument,
  8. [HiReply::class, 'decode']
  9. );
  10. }
  11. }

gRPC 客户端还支持 gRPC 的 Streaming 模式。以双向流为例:

  1. <?
  2. public function hello()
  3. {
  4. $client = new RouteGuideClient('127.0.0.1:50051');
  5. $note = new RouteNote();
  6. $call = $client->routeChat();
  7. $call->push($note);
  8. $call->push($note);
  9. /** @var RouteNote $note */
  10. [$note,] = $call->recv();
  11. [$note,] = $call->recv();
  12. }

请注意在 streaming 模式下,您必须手动捕获连接断开的异常 (Hyperf\GrpcClient\Exception\GrpcClientException) 并根据需要选择是否重试。

写在后面

如果你是 gRPC 的重度使用者, 欢迎关注 hyperf 的后续开发者工具, 可以根据 .proto 文件生成全套 gRPC 代码.