[scode type="yellow"]首先声明,截至文章日期为止,并没有发布php的grpc服务端(据说是因为php不是常驻内存的原因),所以使用golang的服务端代替,反正不影响使用就行,另外如果你喜欢其他语言也可以,例如node做server。[/scode]

一、首先安装golang并设置环境变量

安装方法看《CentOS安装/更新Golang 1.9以上版本》,最好安装1.9及以上的版本,然后设置变量,如下:

vi /etc/bashrc

内容

export GOPATH=/home/Go
export GOROOT=/home/go
export PATH=$PATH:$GOROOT/bin

使环境变量生效

source /etc/bashrc

创建GOHOME目录

mkdir /home/Go

二、安装golang grpc

golang protobuf 库

go get -u github.com/golang/protobuf/proto

protoc --go_out 工具

go get -u github.com/golang/protobuf/protoc-gen-go

如果可以翻墙:

go get google.golang.org/grpc

否则:
[scode type="share"]

mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/net.git --depth 1
git clone https://github.com/golang/text.git --depth 1
git clone https://github.com/golang/sys.git --depth 1

建立相关目录,进入目录

mkdir -p $GOPATH/src/google.golang.org/
cd $GOPATH/src/google.golang.org
git clone https://github.com/google/go-genproto.git genproto --depth 1

从Github上克隆grpc仓库

git clone https://github.com/grpc/grpc-go.git grpc

安装仓库

cd $GOPATH/src/
go install google.golang.org/grpc

[/scode]

三、运行go grpc

运行go grpc服务端和客户端

1、服务端

编译服务端

cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_server
go build -o greeter_server main.go

运行服务端

./greeter_server &

2、客户端

编译客户端

cd  $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_client
go build -o greeter_client  main.go

运行客

./greeter_client 

出现如下信息证明成功
[scode type="share"]2019/01/08 18:18:54 Greeting: Hello world[/scode]

四、安装php grpc客户端

[post cid="201" /]

五、创建proto文件

创建helloworld.proto文件,内容如下:

syntax = "proto3";
package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

生成相关包文件

protoc --proto_path=/var/www/html/mysite --php_out=/var/www/html/mysite --grpc_out=/var/www/html/mysite --plugin=protoc-gen-grpc=/var/www/html/grpc/bins/opt/grpc_php_plugin /var/www/html/mysite/helloworld.proto

此时目录下会生成 GPBMetadata 和 Helloworld 两个目录

六、创建composer文件

创建composer.json文件,内容如下:

{
  "name": "grpc/grpc-demo",
  "description": "gRPC example for PHP",
  "require": {
    "grpc/grpc": "^v1.3.0",
    "google/protobuf": "^v3.3.0"
  },
  "autoload": {
    "psr-4": {
      "GPBMetadata\\": "GPBMetadata/",
      "Helloworld\\": "Helloworld/"
    }
  }
}

生成自动加载及公共类库vendor

composer install

注意:因为在composer.json里面已经指定了GPBMetadata、Helloworld的加载为止,所以composer install或者composer update之后不用单独包含这两个目录里面的类文件了,因为已经autoload自动加载。

七、php客户端调用

例子 1:

<?php
require dirname(__FILE__). '/vendor/autoload.php';
// 50051端口随意是什么都可以,不过要与服务端监听的端口一致
$client = new Helloworld\GreeterClient('127.0.0.1:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
$request = new Helloworld\HelloRequest();
$request->setName('hahahaha');
list($reply, $status) = $client->SayHello($request)->wait();
echo $reply->getMessage();exit;

例子2:
proto文件

syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 接收提交的参数
message HelloRequest {
    string id = 1;
    string name = 2;
}
// 为下面返回数据定义详细的格式
message Result {
    int32 id = 1;
    int32 age = 2;
    int32 gender = 3;
    string name = 4;
}
// 返回的数据
message HelloReply {
  repeated Result result = 1;
}

php客户端代码

<?php
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
require dirname(__FILE__). '/vendor/autoload.php';
// 需要返回的数据
$result = array(
    array (
        'id' => 1,
        'age' => 22,
        'gender' => 1,
        'name' => 'zhangsan'
    ),
    array (
        'id' => 2,
        'age' => 27,
        'gender' => 2,
        'name' => 'lijing'
    )
);
// 打开gRPC端口
$client = new Helloworld\GreeterClient('127.0.0.1:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
// proto使用repeated类型数据格式,那么需要这个类,repeated类型数据格式主要是适用与重复出现的数据,例如N个用户的列表
$arr = new RepeatedField(GPBType::MESSAGE, Helloworld\Result::class);
// 这个是我自己定义的,为了偷懒,并不一定适用所有情景,你也可以使用SetXxx()、GetXxx()来代替
function func($key, $type = 'set')
{
    $key = explode('_', $key);
    $key = implode('', array_map('ucfirst', $key));
    return $type . ucfirst($key);
}
foreach ($result as $info) {
    $res = new \Helloworld\Result();
    foreach ($info as $key => $val) {
        $func = func($key, 'set');
        $res->$func($val);
    }
    $arr[] = $res;
}
$request = new \Helloworld\HelloRequest();
$request->setId(1);
$request->setName('zhangsan');
list($reply, $status) = $client->SayHello($request)->wait();
// 提交给服务端
$reply->setResult($arr);
if (is_object($reply) && $status->code == 0) {
    $list = array();
    foreach ($reply->getResult() as $val) {
        $info = array();
        foreach (array_keys($result[0]) as $key) {
            $func = func($key, 'get');
            $info[$key] = $val->$func();
        }
        $list[] = $info;
    }
    echo '
';print_r($list);exit;
    }
    else {
        echo $status->details;
    }


  [1]: https://www.kyzy.cc/206.html