

grpc的Go服务端和PHP客户端实现
source link: https://www.tuicool.com/articles/vYVrMj2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
本例系统为 CentOS Linux release 7.5.1804 (Core) ,具体实现如下:
安装GO(已安装跳过)
1、安装yum 源
yum install epel -y
2、然后使用 yum 安装 Golang:
yum install go -y
查看版本
go version #go version go1.9.4 linux/amd64
3、配置环境变量
在 /etc/profile 添加:
export GOPATH=/home/go export PATH=$PATH:$GOPATH/bin
然后执行 source /etc/profile 使之生效,创建GOPATH目录
mkdir /home/go
安装protobuf
1、安装相关软件
协议编译器是用C ++编写的。如果您使用的是C ++,请按照C ++安装说明在C ++运行时安装protoc。
yum install autoconf automake libtool gcc gcc-c++ zlib-devel
2、 下载protobuf,并安装
去到 Protocol Buffers 下载最新版本(Version3.0.0 beta2),然后解压到本地。
tar -zxvf protobuf-all-3.5.1.tar.gz cd protobuf-3.5.1 ./configure make make install
3、查看protobuf 版本
protoc --version
显示 libprotoc 3.5.1 ,证明成功。
4、然后安装golang protobuf直接使用golang的get即可
go get -u github.com/golang/protobuf/proto // golang protobuf 库 go get -u github.com/golang/protobuf/protoc-gen-go //protoc --go_out 工具
安装golang的grpc包
能翻墙的同学执行以下命令的就可以简单实现
go get google.golang.org/grpc
不能翻墙同学也别急,也能实现,解决办法如下。
1、grpc需要一下依赖:crypto net oauth2 sys text tools,不安装会报错。
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 git clone https://github.com/golang/crypto.git --depth 1 git clone https://github.com/golang/oauth2.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
2、从Github上克隆其他的仓库
cd $GOPATH/src/google.golang.org git clone https://github.com/grpc/grpc-go.git grpc --depth 1
命令解析:
其中--depth=1 这个参数的意思是只克隆最新的commit分支。不加也行。
最后的grpc表示的是将克隆的文件存放到那个文件夹里面。
执行完上面的命令,我们就成功的将grpc的包下载到本地了。
3、 安装仓库
cd $GOPATH/src/ go install google.golang.org/grpc
安装php的grpc扩展
1、下载地址: http://pecl.php.net/package/gRPC
wget http://pecl.php.net/get/grpc-1.12.0.tgz tar -zxvf grpc-1.12.0.tgz cd grpc-1.12.0 phpize ./configure --with-php-config=/usr/bin/php-config make make install
或用pecl方式安装
pecl install grpc
添加grpc.so到php.ini配置
vim /etc/php.ini extension = "grpc.so" php -m | grep "grpc"
出现 grpc 证明安装成功
安装 protobuf 及其 php 扩展
为了使用gRPC获得更好的性能,请启用protobuf C扩展。
protobuf.so使用PECL 安装扩展。
pecl install protobuf
现在将此行添加到您的php.ini文件中,例如 /etc/php.ini。
extension=protobuf.so
创建proto文件userrpc.proto
在目录/home/go/src/userrpc,下新建userrpc.proto
syntax = "proto3"; package user; option go_package = "./grpc/user"; // The User service definition. service User { // Get all Users with id - A server-to-client streaming RPC. rpc GetUsers(UserFilter) returns (stream UserRequest) {} // Create a new User - A simple RPC rpc CreateUser (UserRequest) returns (UserResponse) {} } // Request message for creating a new user message UserRequest { int32 id = 1; // Unique ID number for a User. string name = 2; string email = 3; string phone= 4; message Address { string province = 1; string city = 2; } repeated Address addresses = 5; } message UserResponse { int32 id = 1; bool success = 2; } message UserFilter { int32 id = 1; }
option go_package = "./grpc/user"; 为pb.go文件生成的目录。
syntax = "proto3";说明本教程中的示例使用协议缓冲区语言的proto3版本。
grpc四种服务类型:
1、简单方式:这就是一般的rpc调用,一个请求对象对应一个返回对象
2、服务端流式(Sever-side streaming )
3、客户端流式(Client-side streaming RPC)
4、双向流式(Bidirectional streaming RPC)
之所以用到stream,针对当业务需要传输大量的数据时(或者客户端向服务器传输大量数据,或反之,或者双向需要传输大量数据),数据的传输时间可能有些长,接收端需要收到所有的数据后才能继续处理,而不能一边接收数据一边处理数据。
这里我们采用了其中两种:
// Get all Users with id - A server-to-client streaming RPC.
rpc GetUsers(UserFilter) returns (stream UserRequest) {}
// Create a new User - A simple RPC
rpc CreateUser (UserRequest) returns (UserResponse) {}
使用方式就是前面加 stream 标记。如stream UserRequest
如想了解其他示例,请阅读 http://www.grpc.io/docs/
Protobuf3语言指南
可参考: https://blog.csdn.net/u011518...
编译 .proto 文件
先生成Go代码,也可以和php一起生成,这里分开执行。
先创建生成Go代码的目录
cd /home/go/src/userrpc mkdir grpc/user
如果需要将其以 gRPC 的方式提供服务的话,需需要在编译时指定插件(--go_out=plugins=grpc:output)。
执行命令:
protoc --go_out=plugins=grpc:. userrpc.proto
查看./grpc/user/目录下新建了一个userrpc.pb.go
新建服务端server.go
package main import ( "log" "net" "golang.org/x/net/context" "google.golang.org/grpc" pb "userrpc/grpc/user" ) const ( port = ":50051" ) // server is used to implement user.UserServer. type server struct { savedUsers []*pb.UserRequest } // CreateUser creates a new User func (s *server) CreateUser(ctx context.Context, in *pb.UserRequest) (*pb.UserResponse, error) { s.savedUsers = append(s.savedUsers, in) return &pb.UserResponse{Id: in.Id, Success: true}, nil } // GetUsers returns all users by given id func (s *server) GetUsers(filter *pb.UserFilter, stream pb.User_GetUsersServer) error { for _, user := range s.savedUsers { if filter.Id == 0 { continue } if err := stream.Send(user); err != nil { return err } } return nil } func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } // Creates a new gRPC server s := grpc.NewServer() pb.RegisterUserServer(s, &server{}) s.Serve(lis) }
客户端client.go
package main import ( "io" "log" "golang.org/x/net/context" "google.golang.org/grpc" pb "userrpc/grpc/user" ) const ( address = "localhost:50051" ) // createUser calls the RPC method CreateUser of UserServer func createUser(client pb.UserClient, user *pb.UserRequest) { resp, err := client.CreateUser(context.Background(), user) if err != nil { log.Fatalf("Could not create User: %v", err) } if resp.Success { log.Printf("A new User has been added with id: %d", resp.Id) } } // getUsers calls the RPC method GetUsers of UserServer func getUsers(client pb.UserClient, id *pb.UserFilter) { // calling the streaming API stream, err := client.GetUsers(context.Background(), id) if err != nil { log.Fatalf("Error on get users: %v", err) } for { user, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.GetUsers(_) = _, %v", client, err) } log.Printf("User: %v", user) } } func main() { // Set up a connection to the gRPC server. conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() // Creates a new UserClient client := pb.NewUserClient(conn) user := &pb.UserRequest{ Id: 1, Name: "test", Email: "[email protected]", Phone: "132222222", Addresses: []*pb.UserRequest_Address{ &pb.UserRequest_Address{ Province: "hebei", City: "shijiazhuang", }, }, } // Create a new user createUser(client, user) // Filter with an id filter := &pb.UserFilter{Id: 1} getUsers(client, filter) }
目录结构
#tree -L 1 ├── client.go ├── grpc ├── server.go └── userrpc.proto
运行 gRPC 服务
启动server.go
go run server.go
新打开一个窗口,启动client.go
go run client.go
结果为:
2019/07/04 17:01:16 A new User has been added with id: 1
2019/07/04 17:01:16 User: id:1 name:"test" email:"[email protected]" phone:"132222222" addresses:<province:"hebei" city:"shijiazhuang" >
自此一个简单的 gRPC 服务就搭建起来了。
接下来我们实现php语言客户端和go服务端通信
安装 grpc_php_plugin 插件
grpc_php_plugin 插件可以帮我们自动生成(client stub)客户端(封装了 grpc
的服务接口),方便我们直接调用。
# 下载 grpc 的库到本地 cd ~ && git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc # 更新子模块依赖 cd grpc && git submodule update --init # 这里我们只编译 php 的插件 如果要编译所有的 make && make install make grpc_php_plugin # 插件路径 ll ./bins/opt/grpc_php_plugin
生成php客户端基类(client stub 类)
#创建user目录 mkdir user #tree -L 1 ├── client.go ├── grpc ├── server.go ├── user └── userrpc.proto
执行编译.proto命令
protoc --php_out=./user --grpc_out=./user --plugin=protoc-gen-grpc=/root/grpc/bins/opt/grpc_php_plugin userrpc.proto
浏览user目录
#tree ├── GPBMetadata │ └── Userrpc.php └── User ├── UserClient.php ├── UserFilter.php ├── UserRequest │ └── Address.php ├── UserRequest_Address.php ├── UserRequest.php └── UserResponse.php
这个时候你会发现生成了一个UserClient.php 文件,这个文件就是php客户端基类文件。
注意:如果运行 以下命令是不会生成UserClient.php 文件的
protoc --php_out=plugins=grpc:./user userrpc.proto
使用 composer
管理依赖加载
没有安装composer,先安装
curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer
在user目录下创建composer.json
{ "name": "grpc-go-php", "require": { "grpc/grpc": "^v1.12.0", "google/protobuf": "^v3.5.0" }, "autoload":{ "psr-4":{ "GPBMetadata\\":"GPBMetadata/", "User\\":"User/" } } }
注意:需要说明的是 "google/protobuf": "^v3.5.0"不是必须的,可以去掉,这个是为了你在没有安装php的protobuf扩展情况下,也能正常运行,这种运行方式相对效率较低。
相关依赖grpc、protobuf的最新版本可参考: https://github.com/grpc/grpc/...
安装
composer install
这时user目录下
# tree -L 1 ├── composer.json ├── composer.lock ├── GPBMetadata ├── User └── vendor
在user创建php客户端client.php
<?php require_once __DIR__ . '/vendor/autoload.php'; use User\UserClient; // 创建客户端实例 $userClient = new UserClient('127.0.0.1:50051', [ 'credentials' => Grpc\ChannelCredentials::createInsecure() ]); //处理添加用户 rpc CreateUser (UserRequest) returns (UserResponse) {} $address = new User\UserRequest\Address(); $address->setCity("xian"); $address->setProvince("shanxi"); $userRequest = new User\UserRequest(); $userRequest->setId(3); $userRequest->setEmail("[email protected]"); $userRequest->setName("demo"); $userRequest->setPhone("13000000000"); $userRequest->setAddresses([$address]); $request = $userClient->CreateUser($userRequest)->wait(); //返回数组 //$response 是 UserResponse 对象 //$status 是数组 list($response, $status) = $request; foreach ($response as $k=>$v){ echo 'id=>'.$v->getId(),"\n\r"; } //处理获取用户 rpc GetUsers(UserFilter) returns (stream UserRequest) {} //设置请求参数UserFilter $userFilter = new User\UserFilter(); $userFilter->setId(1); $call = $userClient->GetUsers($userFilter); $features = $call->responses(); foreach ($features as $feature) { echo "<pre>"; var_dump( $feature->getName()); var_dump( $feature->getId()); foreach ($feature->getAddresses() as $v) { var_dump($v->getProvince()); var_dump($v->getCity()); } echo "</pre>"; // process each feature } // the loop will end when the server indicates there is no more responses to be sent.
新打开一个窗口
运行client.php
php client.php
结果:
<pre>string(4) "demo"
int(3)
string(6) "shanxi"
string(4) "xian"
</pre>
证明Go为服务端,php为客户端的grpc服务搭建完成。
小结:
优点
Grpc使用http2协议,故支持http2的全双工,多路复用等特性,基于HTTP/2 多语言客户端实现容易。
Grpc使用protobuf作为序列化工具,具有序列化效率高,压缩数据体积小等优点。
缺点
Api实现起来比较繁琐,给开发带来难度。
总的来说Grpc是一个不错的跨语言rpc解决方案,当然每个人都自己的看法或见解。针对不同的业务场景采用不同的解决方案,最终都是运行效率和开发效率的相互妥协的结果。
参考资料:
官方网站: http://www.grpc.io/
官方文档: http://www.grpc.io/docs/
中文翻译: http://doc.oschina.net/grpc
links
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK