内容简介: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
前言
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: "fasd@163.com", 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:"fasd@163.com" 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("demo@163.com"); $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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 支付宝客户端架构解析:iOS 客户端启动性能优化初探
- 自己动手做数据库客户端: BashSQL开源数据库客户端
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 客户端HTTP缓存
- 简析移动客户端安全
- 配置Hadoop集群客户端
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解计算机系统(原书第3版)
Randal E.Bryant、David O'Hallaron / 龚奕利、贺莲 / 机械工业出版社 / 2016-11 / 139.00元
和第2版相比,本版内容上*大的变化是,从以IA32和x86-64为基础转变为完全以x86-64为基础。主要更新如下: 基于x86-64,大量地重写代码,首次介绍对处理浮点数据的程序的机器级支持。 处理器体系结构修改为支持64位字和操作的设计。 引入更多的功能单元和更复杂的控制逻辑,使基于程序数据流表示的程序性能模型预测更加可靠。 扩充关于用GOT和PLT创建与位置无关代码的......一起来看看 《深入理解计算机系统(原书第3版)》 这本书的介绍吧!