php作为客户端调用golang grpc服务(目前php无法用做grpc的服务端,但是作为客户端是没有问题的)

开发环境(本地):windows+docker(镜像:php:7.3-fpm-alpine)

框架:laravel 5.8

1、首先安装grpc服务,由于docker使用alpine镜像,所以直接使用apk add grpc可快速安装,安装后输入protoc --version看是否正常,源码编译安装由于需要下载github上面很多的第三方包导致非常慢,我的是一直无法安装;容器中也可以不安装,在本地开发机器上面安装(注意版本:最新版本3.14和3.15在本地windows上一直无法正常生成php文件,总是乱码,我使用的是3.12.2),然后生成php客户端文件之后直接放入项目中

2、安装php grpc扩展和protobuf扩展(尽量不要最新版本,在这里卡了好久,新版本一直无法安装成功,后来使用旧版本后成功安装)

docker-file:
&& pecl install grpc-1.26.0 \
&& docker-php-ext-enable grpc \
&& pecl install protobuf-3.10.0 \
&& docker-php-ext-enable protobuf

使用phpinfo查看是否正常安装了这两个扩展

3、项目文件composer.json中引入grpc和protobuf(在项目app下新增文件夹Grpc及Proto,用于存放proto文件和及转换后的php文件)

运行:composer install

 

4、编写proto文件

syntax = "proto3";

option go_package = ".;user";
package user;

//用户中心定义
service UserService {
	//操作用户积分和余额
    rpc ApiUser(ApiUserRequest) returns (Response) {
    }
}

message ApiUserRequest {
	int64 type = 1;
	int64 uid = 2;
	int64 num = 3;
	string sign = 4;
}

//通用响应数据
message Response {
    int64 code = 1;
    string message = 2;
	int64 time = 3;
	map<string,string> data = 4;
}

将文件放入项目proto文件夹中,并在该文件夹下运行命令:

protoc --php_out=../Grpc/UserCenter --grpc_out=../Grpc/UserCenter --plugin=protoc-gen-grpc=/usr/bin/grpc_php_plugin userService.proto

会在Grpc/UserCenter下生成以下几个文件

注意:grpc_php_plugin插件,第一步安装完grpc后一般都会在/usr/bin中生成此文件,如果缺失可以更换命令:protoc --php_out=../Grpc/UserCenter userService.proto,但将无法生成UserServiceClient.php文件,不过客户端文件内容非常简单,可以自己参考以下编写

// GENERATED CODE -- DO NOT EDIT!

namespace User;

/**
 * 用户中心定义
 */
class UserServiceClient extends \Grpc\BaseStub {

    /**
     * @param string $hostname hostname
     * @param array $opts channel options
     * @param \Grpc\Channel $channel (optional) re-use channel object
     */
    public function __construct($hostname, $opts, $channel = null) {
        parent::__construct($hostname, $opts, $channel);
    }

    /**
     * 操作用户积分和余额
     * @param \User\ApiUserRequest $argument input argument
     * @param array $metadata metadata
     * @param array $options call options
     */
    public function ApiUser(\User\ApiUserRequest $argument,
      $metadata = [], $options = []) {
        return $this->_simpleRequest('/user.UserService/ApiUser',
        $argument,
        ['\User\Response', 'decode'],
        $metadata, $options);
    }
}

 

 

5、最后在项目中调用客户端类

public function client(){
        $client = new \User\UserServiceClient('192.168.10.41:16000',[
           'credentials' => \Grpc\ChannelCredentials::createInsecure()
        ]);

        $request = new \User\ApiUserRequest();
        $request->setType(1);
        $request->setUid(111);
        $request->setNum(1341);
        $request->setSign("asfasdf");

        $get = $client->ApiUser($request)->wait();

        list($reply,$status) = $get;

        if ($status->code !== \Grpc\STATUS_OK) {
            throw new \Exception($status->details,$status->code);
        }

        if($reply->getCode() != 200){
            throw new \Exception($reply->getMessage(),$reply->getCode());
        }
        
        // getData返回的是MapField类,直接用dump是看不到具体结果的
        foreach ($reply->getData() as $k=>$v){
            dump($k."=>".$v);
        }
        // 也可使用以下方式获取具体键值内容
        dump($reply->getData()->offsetGet("a"));
}

结果展示: