恥知らずのウェブエンジニア -web engineer, shameless

これは一歩を踏み出すことができない者たちのブログ

PHPでgRPC叩く手始め

なんかもうアレなので、PHPでgRPCのサーバーを叩いてみる。まずは環境整備から

  • 流れ的にはprotobufを定義して、それを元にPHPからgRPCと通信するclientを作ってそれを使ってgPRCを叩く模様

Environment

grpc

# peclが必要
yum install --enablerepo=remi-php71 php-pear gcc-c++ make zlib-devel php-devel

pecl install grpc
echo 'extension=grpc.so' >> /etc/php.ini

参考: https://github.com/grpc/grpc/issues/11957 https://github.com/grpc/grpc/issues/11057

protobuff

pecl install protobuf
echo 'extension=protobuf.so' >> /etc/php.ini    

# 現時点の最新版protobuf-3.5.0.1は下記のエラーが出るのでv3.4を使う。。。
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib64/php/modules/protobuf.so' - /usr/lib64/php/modules/protobuf.so: undefined symbol: timelib_update_ts in Unknown on line 0

See : https://github.com/google/protobuf/issues/3929

# 一旦protobuf-3.4.0 install
pecl uninstall protobuf
pecl install protobuf-3.4.0

# protobufのコンパイラは下記から
https://github.com/google/protobuf/releases/

grpc用のprotobuf plugin

#repo clone
git clone --recursive -b v1.7.x https://github.com/grpc/grpc

# 公式には書いてないけどこれやらないと下のmakeができないっぽい
cd grpc/third_party/protobuf
./autogen.sh

cd grpc
make grpc_php_plugin

※上手くいかない時は下記が足りていないかも
libtool
shtool
autoconf
autogen

サンプルproto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayHelloAgain (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;
}
  • phpのコード生成
protoc --proto_path=examples/protos   --php_out=examples/php   --grpc_out=examples/php   --plugin=protoc-gen-grpc=bins/opt/grpc_php_plugin   ./examples/protos/helloworld.proto
  • sampleを実行してみる
require dirname(__FILE__).'/vendor/autoload.php';

@include_once dirname(__FILE__).'/Helloworld/GreeterClient.php';
@include_once dirname(__FILE__).'/Helloworld/HelloReply.php';
@include_once dirname(__FILE__).'/Helloworld/HelloRequest.php';
@include_once dirname(__FILE__).'/GPBMetadata/Helloworld.php';

function greet($name)
{
    $client = new Helloworld\GreeterClient('localhost:50051', [
        'credentials' => Grpc\ChannelCredentials::createInsecure(),
    ]);

    $request = new Helloworld\HelloRequest();
    $request->setName($name);
    list($reply, $status) = $client->SayHello($request)->wait();
var_dump($reply);
var_dump($status);

    return $message;
}

$name = !empty($argv[1]) ? $argv[1] : 'world';
echo greet($name)."\n";


[root@6b0b0fc608dc php]# php greeter_client.php wow
/var/workspace/grpc/examples/php/greeter_client.php:42:
NULL
/var/workspace/grpc/examples/php/greeter_client.php:43:
class stdClass#12 (3) {
  public $metadata =>
  array(0) {
  }
  public $code =>
  int(14)
  public $details =>
  string(14) "Connect Failed"
}
PHP Fatal error:  Uncaught Error: Call to a member function getMessage() on null in /var/workspace/grpc/examples/php/greeter_client.php:44
Stack trace:
#0 /var/workspace/grpc/examples/php/greeter_client.php(53): greet('wow')
#1 {main}
  thrown in /var/workspace/grpc/examples/php/greeter_client.php on line 44

それっぽく動いてる? ※grpc serverはnodeで動くので立ち上げていない

次は実際にgRPCサーバーを立てて動作確認