php使用grpc服务(从0开始,并且包含mapfield文件解释) grpc是面对微服务框架而风生水起的,上次我用python编写了一个图神经网络处理的微服务,使用grpc放在我的服务器本地端口上。
现在我希望我的一个php项目也可以调用该服务,现在来试一试吧~
流程 php的服务器安装protoc php的服务器安装grpc 编写服务端代码编写客户端代码 由于服务端(python)的代码已经编写或者说已经部署,就不做叙述了。
安装代码请根据自己的php版本和grpc版本酌情自定义。本人使用的php8.0,grpc1.62.0,protobuf4.62.0
安装protoc解释器 和windows开发一样,使用grpc服务均需要使用protoc解释器,去官方github下的release下载linux版本:
protoc-26.0-linux-x86_64.zip
解压到 /usr/bin/目录下
如果你使用宝塔,你也可以直接先在本地电脑上解压,把解压后的bin文件夹里的protoc文件上传到 /usr/bin/中就好了。
终端中输入 protoc,有返回即为成功
安装grpc 无论你服务器是否安装pecl,可以直接通过http请求安装php相关包,但是必须安装了php(废话)
# 下载解压 grpc
cd ~
wget http://pecl.php.net/get/grpc-1.62.0.tgz
tar xvf grpc-1.62.0.tgz
cd grpc-1.62.0
# 生成配置并编译安装(编译安装时间很长,我大概安装了一小时左右)
# 注意选择你自己的路径以及php版本,我是80
/www/server/php/80/bin/phpize
./configure --with-php-config= /www/server/php/80/bin/php-config
make && make install 之后要配置php的拓展
注意,这是必要的,compose安装的grpc依赖,底层还是调用的这个grpc拓展!
# 配置PHP扩展
cd grpc-1.62.0
echo "extension = grpc.so" >> /www/server/php/80/etc/php.ini
cd protobuf-4.62.0 # 如果没有路径请仿照grpc安装的方式手动安装安装一下,我个人觉得可能并不需要
echo "extension = protobuf.so" >> /www/server/php/80/etc/php.ini 最后重启一下php和nginx服务就大功告成了
编译protoc文件 具体的protoc文件的定义详细见我之前的博客
需要安装protoc和grpc_php_plugin
使用如下代码生成:
protoc --php_out ./ you-file.proto #需要安装protoc解释器,生成protoc的php定义文件在当前目录(./)
protoc --grpc_out ./ you-file.proto #需要grpc_php_plugin插件安装,生成grpc文件在当前目录 第一行生成你的proto数据定义文件,我生成了 GCNResult.php,Node.php,Edge.php,GraphData.php,
同时还会生成一个GPBMetaData文件夹。
第二行生成php的grpc文件:GCNServiceClient.php
注意,如果你没有生成grpc文件的插件(安装grpc出现问题),可以直接下载该插件
然后通过如下代码生成 xxxClient.php文件
protoc --grpc_out ./ --plugin= protoc-gen-grpc= /your-path-to-plugin/grpc_php_plugin you-filename.proto
:: 自用
:: protoc --grpc_out ./ --plugin= protoc-gen-grpc= /opt/share/grpc_php_plugin gcn.proto 编写php请求的代码(客户端代码) 编写文件前置注意事项 注意:如果你使用宝塔,需要把php设置里的禁用函数 putenv和 proc_open给删除,不然composer安装无法进行。 需要编写composer.json文件,因为使用了 dirname(__FILE__).'/vendor/autoload.php'该自动导入功能。json文件内容示例: {
"require" : {
"grpc/grpc" : "*" ,
"google/protobuf" : "*"
},
"autoload" : {
"psr-4" : {
"GPBMetadata\\" : "protoc/GPBMetadata/" ,
"protoc\\" : "protoc/"
}
}
} 编写后在服务器该文件目录下启动终端输入 composer install即可,会生成vendor文件夹
现在我将编写一个最简单的php文件来调用这个服务。
<? php
require dirname (__FILE__ ). '/vendor/autoload.php' ; // 引入 gRPC PHP 扩展的自动加载文件
require 'protoc/GraphData.php' ; // 引入包含 protoc文件夹下的grpc生成文件
require 'protoc/Node.php' ;
require 'protoc/Edge.php' ;
require 'protoc/GCNResult.php' ;
require 'protoc/GCNServiceClient.php' ;
// 进行grpc请求,获取gcn处理后的数据,返回json字符串
function GCN_request ()
{
$client = new GCNServiceClient ('localhost:9999' , [
'credentials' => \Grpc\ChannelCredentials :: createInsecure (),
]);
// 创建一个实例的图数据
$G_example = new GraphData ();
$G_example-> setNodes ([
(new Node ())-> setId ("node1" )-> setFeatures ([0.1 , 0.2 , 0.3 ]),
(new Node ())-> setId ("node2" )-> setFeatures ([0.4 , 0.5 , 0.6 ]),
]);
$G_example-> setEdges ([
(new Edge ())-> setSourceId ("node1" )-> setTargetId ("node2" ),
]);
// 发送请求并接收响应
list ($response, $status) = $client-> ProcessGraph ($G_example)-> wait ();
if ($status-> code !== Grpc\STATUS_OK ) {
// gRPC 请求出错
throw new Exception ('Error calling grpc server -> ProcessGraph: ' . $status-> details );
exit (1 );
}
// 因为我的返回结果是个map数据类型,php中没有该类型,需要做一个遍历取值,如果是string类型可以直接取。
$NodeScores = [];
foreach ($response-> getNodeScores () as $key => $value) {
$NodeScores[$key] = $value;
}
return json_encode ($NodeScores);
} 该函数返回一个json数据,想要修改可以使用 json_decode() , 至此,大功告成!
备注 因为我的protoc文件的返回结果定义为一个map,这是go才有的数据结构,php没有
使用 var_dump($response->getNodeScores())可以看到这是谷歌grpc核心中定义的一个mapfield object,可以直接像go的map一样,用 $response->getNodeScores()['xxx']获取值。
虽然官网显示有ToString()的方法:https://cloud.google.com/dotnet/docs/reference/Google.Protobuf/latest/Google.Protobuf.Collections.MapField-2#Google_Protobuf_Collections_MapField_2_ToString
但是在这里无法使用,所以我只能通过遍历的方式获取所有值存到一个数组里,毕竟map结构本身就不支持一次获取全部,还是要遍历。