PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信
# 概述
从零开始讲解,PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信。因为我本地环境都是配置好的,避免我落下步骤操作,所以我在docker环境下开发,拉取一个基于Alpine的镜像。Alpine操作系统是一个面向安全的轻型 Linux 发行版。
# 正文
搭建环境
1.项目中,我会用到composer以及PHP相关的扩展,于是我拉取一个基于PHP7.2的docker镜像。命令中的参数,可以看我另一篇文章,Docker 使用笔记-常用基础命令。
docker container run -it --name grpc-ubuntu -v /Users/wuerzhilv-lq/examples:/home/examples lorisleiva/laravel-docker:7.2 /bin/bash
2.安装相关的包
apk add autoconf automake libtool linux-headers
3.这个docker没有golang语言环境,我们这里安装下。这里采用编译安装的方式,为什么不用apk包管理器安装,因为Go工具链是用Go编写的,要构建它,需要安装Go编译器;由于我们没有GO编译器,同时1.4以后的GO语言版本没有直接支持GCC,支持GO编译器和GCCGO,因此我们需要先现在1. 4版本的GO,利用GCC编译好GO编译器后,再利用1.4版本的GO编译器编译最新版本的GO(比如以下的1.14)。
①下载go1.4版本,源码从官方下载
wget https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz
tar -zxf go1.4-bootstrap-20171003.tar.gz
mv go /usr/local/go1.4
2
3
4
②下载go1.14版本
wget https://dl.google.com/go/go1.14.2.src.tar.gz
tar -zxf go1.14.2.src.tar.gz
mv go /usr/local/go
2
3
4
③环境变量设置(GOROOT_BOOTSTRAP是编译器环境设定需要)
echo 'export GOROOT_BOOTSTRAP=/usr/local/go1.4' >> ~/.bash_profile
echo 'export GOROOT=/usr/local/go' >> ~/.bash_profile
echo 'export PATH=${GOROOT}/bin:${PATH}' >> ~/.bash_profile
//重载环境配置变量(主要是重载GOROOT_BOOTSTRAP)
source ~/.bash_profile
2
3
4
5
6
④ 编译go1.4的版本
cd /usr/local/go1.4/src && ./make.bash
2
⑤利用go1.4的go编译器,编译go1.14
cd /usr/local/go/src && ./make.bash
2
⑥查看golang版本
go version
2
4.新下载的php本来就没有现成的php.ini文件。只是给了php.ini-development (开发环境用)与 php.ini-production(生产环境用)两个建议。这里我们基于生产环境,复制出一个php.ini文件。
cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
5.系统层面的,我们环境就安装好了。Grpc 是使用protobuf来传输数据,protobuf是一个文件,那怎么让php和golang语言识别它,这里就需要一个编译工具了。
apk add protobuf
- PHP安装Grpc,Protobuf扩展。
pecl install grpc-1.25.0
pecl install protobuf-3.11.4
2
打开我们新建的php.ini文件,将扩展加进去。可以使用php -m 查看配置是否生效。
extension=grpc.so
extension=protobuf.so
2
7.安装PHP Protoc Plugin
第一种方法:
$ git clone -b v1.28.1 https://github.com/grpc/grpc
$ cd grpc
$ git submodule update --init
$ make grpc_php_plugin
2
3
4
make编译后,输出目录为bins/opt,输出文件grpc_php_plugin,将文件复制到/home/examples
cp bins/opt/grpc_php_plugin /home/examples/
第二种方法:
bazel build src/compiler:grpc_php_plugin
8.安装Golang Protoc Plugin
go get -u github.com/golang/protobuf/protoc-gen-go
export PATH=$PATH:$GOPATH/bin
2
9.安装grpc
go get -u google.golang.org/grpc
10.环境都配好了,我们来写项目代码,我在创建docker容器的时候,就把容器目录挂载到了本地,可以使用ide工具开发。
我这里配置的本地目录是,/Users/wuerzhilv-lq/examples。examples就相当于项目目录了,在这个项目中新建protos目录,用于存放protobuf文件;新建php目录用于存放客户端代码;新建go目录,用于存放服务端代码。
我们新建一个protobuf文件(helloworld.proto),来定义服务方法,请求参数,请求返回值。 内容如下
// 定义语法,一共是有两种proto2,proto3。我这里使用proto3。
syntax = "proto3";
// 定义服务(我感觉像类)
service Greeter {
// 定义服务方法为SayHello,可以有多个方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
//定义请求参数
message HelloRequest {
string name = 1;
}
//定义返回参数
message HelloReply {
string message = 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 服务端代码,进入容器中的项目目录,根据protobuf文件,使用proto工具生成服务端(golang)代码到go目录。执行命令之后,会在go文件夹中出现一个proto文件夹,里面包含一个helloworld.pb.go文件。
protoc protos/helloworld.proto --go_out=plugins=grpc:go
在examples/go 文件夹中,新建一个main.go文件,注册helloworld服务。
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "./protos"
)
const (
port = ":50051"
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("服务启动"+port)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
运行go run main.go启动服务,看看服务是否能启动。
12.客户端代码,进入容器中的项目目录,根据protobuf文件,使用proto工具生成客户端(PHP)代码到php目录。执行命令之后,会在php文件夹中出现一些文件。
protoc --proto_path=protos --php_out=php --grpc_out=php --plugin=protoc-gen-grpc=/home/examples/grpc_php_plugin protos/helloworld.proto
更换composer源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
通过composer工具,我们初始化一个composer.json文件。
composer.json内容如下:
{
"name": "grpc/grpc-demo",
"description": "gRPC example for PHP",
"require": {
"grpc/grpc": "^1.27",
"google/protobuf": "^3.11"
},
"authors": [
{
"name": "Li",
"email": "calmliqi@gmail.com"
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
composer install 安装扩展包。
在php目录下,新建客户端文件,greeter_client.php
<?php
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();
$message = $reply->getMessage();
return $message;
}
$name = !empty($argv[1]) ? $argv[1] : 'world';
echo greet($name)."\n";
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
13.进入examples项目目录,
启动服务端
go run go/main.go
执行客户端
php php/greeter_client.php
如果输出helloworld,那就是成功了。
# 总结:
这篇文章,只是简单粗略说了下,Grpc+protobuf的使用,比如针对protobuf文件,具体的内容没有说。以后,我会在写一篇针对protobuf文件详细讲解的文章。我懂得也不是太多,就是当自己做了一次总结。如有不对的地方,大家多提意见。我本机挂了代理,所以没有遇到墙的问题,如果被墙了,自行百度吧。 参考了如下资料:
https://developers.google.com/protocol-buffers
https://grpc.io/docs/quickstart
https://golang.org/doc/install/source
https://tkstorm.com/posts-list/programming/go/build-golang-from-source-on-alpine/