一、概述
1.1 gRPC 是什么?
在 gRPC 里客户端应用可以像呼叫本地物件一样直接呼叫另一台不同的机器上服务端 应用的方法,使得您能够更容易地建立分散式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远端呼叫的方法(包含引数和返回型别)。在服务端实现这个界面,并执行一个 gRPC 服务器来处理客户端呼叫。在客户端拥有一个存根能够像服务端一样的方法。gRPC 客户端和服务端可以在多种环境中执行和互动 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支援的语言来编写。所以,你可以很容易地用 Java 建立一个 gRPC 服务端,用 Go、Python、Ruby 来建立客户端。此外,Google 最新 API 将有 gRPC 版本的界面,使你很容易地将 Google 的功能整合到你的应用里。
1.2 资料序列化 -- protocol buffers
gRPC 预设使用 protocol buffers,这是 Google 开源的一套成熟的结构资料序列化机制(当然也可以使用其他资料格式如 JSON)。名叫 proto3 的新风格的 protocol buffers,它拥有轻量简化的语法、一些有用的新功能,并且支援更多新语言。当前针对 Java 和 C++ 释出了 beta 版本,针对 JavaNano(即 Android Java)释出 alpha 版本,在protocol buffers Github 源代码库里有 Ruby 支援, 在golang/protobuf Github 源代码库里还有针对 Go 语言的生成器, 对更多语言的支援正在开发中。1.3为什么使用 gRPC?
有了 gRPC, 我们可以一次性的在一个 .proto 档案中定义服务并使用任何支援它的语言去实现客户端和服务器,反过来,它们可以在各种环境中,从Google的服务器到你自己的平板电脑—— gRPC 帮你解决了不同语言及环境间通讯的复杂性.使用 protocol buffers 还能获得其他好处,包括高效的序列号,简单的 IDL 以及容易进行界面更新。二、gRPC 四类服务
2.1 gRPC 允许定义四类服务方法:
单向 RPC,即客户端传送一个请求给服务端,从服务端获取一个应答,就像一次普通的函式呼叫。rpc SayHello(HelloRequest) returns (HelloResponse){}
服务端流式 RPC,即客户端传送一个请求给服务端,可获取一个数据流用来读取一系列讯息。客户端从返回的资料流里一直读取直到没有更多讯息为止。rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列讯息给服务端。一旦客户端完成讯息写入,就等待服务端读取这些讯息并返回应答。rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
双向流式 RPC,即两边都可以分别通过一个读写资料流来发送一系列讯息。这两个资料流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端讯息,或者它可以先读一个讯息再写一个讯息,或者是读写相结合的其他方式。每个资料流里讯息的顺序会被保持。rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
2.2 RPC 生命周期
现在让我们来仔细了解一下当 gRPC 客户端呼叫 gRPC 服务端的方法时到底发生了什么。我们不究其实现细节,关于实现细节的部分,你可以在我们的特定语言页面里找到更为详尽的内容。单向RPC
首先我们来了解一下最简单的 RPC 形式:客户端发出单个请求,获得单个响应。一旦客户端通过桩呼叫一个方法,服务端会得到相关通知 ,通知包括客户端的元资料,方法名,允许的响应期限(如果可以的话)服务端既可以在任何响应之前直接传送回初始的元资料,也可以等待客户端的请求资讯,到底哪个先发生,取决于具体的应用。一旦服务端获得客户端的请求资讯,就会做所需的任何工作来建立或组装对应的响应。如果成功的话,这个响应会和包含状态码以及可选的状态资讯等状态明细及可选的追踪资讯返回给客户端 。假如状态是 OK 的话,客户端会得到应答,这将结束客户端的呼叫。
服务端流式 RPC
服务端流式 RPC 除了在得到客户端请求资讯后传送回一个应答流之外,与我们的简单例子一样。在传送完所有应答后,服务端的状态详情(状态码和可选的状态资讯)和可选的跟踪元资料被发送回客户端,以此来完成服务端的工作。客户端在接收到所有服务端的应答后也完成了工作。客户端流式 RPC
客户端流式 RPC 也基本与我们的简单例子一样,区别在于客户端通过传送一个请求流给服务端,取代了原先发送的单个请求。服务端通常(但并不必须)会在接收到客户端所有的请求后传送回一个应答,其中附带有它的状态详情和可选的跟踪资料。双向流式 RPC
双向流式 RPC ,呼叫由客户端呼叫方法来初始化,而服务端则接收到客户端的元资料,方法名和截止时间。服务端可以选择传送回它的初始元资料或等待客户端传送请求。 下一步怎样发展取决于应用,因为客户端和服务端能在任意顺序上读写 - 这些流的操作是完全独立的。例如服务端可以一直等直到它接收到所有客户端的讯息才写应答,或者服务端和客户端可以像乒乓球一样:服务端后得到一个请求就回送一个应答,接着客户端根据应答来发送另一个请求,以此类推。三、gRPC实战
3.1 安装gRPC runtime
通过执行下面的命令克隆并安装grpc-go程式码库://克隆grpc程式码库
# git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
//克隆grpc依赖库
# git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
# git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
# git clone https://github.com/golang/sys.git $GOPATH/src/golang.org/x/sys
// 安装protobuf
# go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
# git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
//安装grpc程式码库
# cd $GOPATH/src/
# go install google.golang.org/grpc
3.2 安装protocol buffers
下载protobuf源代码包在github获取protobuf源代码,windows系统可以直接下载exe档案:https://github.com/google/protobuf/releasesmacos和linux环境使用源代码进行安装的步骤# 获取源代码包
wget https://github.com/google/protobuf/archive/v3.5.0.tar.gz
# 解压缩并进入源代码目录
tar -zxvf v3.5.0.tar.gzcd protobuf-3.5.0
# 生成configure档案
./autogen.sh#
编译安装
./configure
make
make check
make install
在执行./autogen.sh过程中可能会因缺乏automake依赖库而报错:autoreconf: failed to run aclocal: No such file or directory,要解决此错误,在linux系统可以通过sudo yum install automake或者sudo apt-get install automake安装automake,在macos系统可以通过brew install automake安装automake。
检测protobuf是否已成功安装 命令列执行protoc --version。windows系统需要将protoc.exe档案所在的目录新增到环境变数。安装golang-protobuf
在golang中安装protobuf相关的库// 安装protobuf
# go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
# git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
基于.proto档案生成grpc资料操作程式码protoc --go_out=plugins=grpc:. route_guide.proto
3.3 定义服务
第一步使用 protocol buffers去定义 gRPC service 和方法 request 以及 response 的型别。要定义一个服务,必须在.proto 档案中指定 service:
service RouteGuide {
...
}
然后在服务中定义 rpc 方法,指定请求的和响应型别,gRPC 允许定义4种类型的 service 方法。
服务.proto档案如下所示:
syntax = proto3;
option java_multiple_files = true;
option java_package = io.grpc.examples.routeguide;
option java_outer_classname = RouteGuideProto;
package routeguide;
// Interface exported by the server.
service RouteGuide {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// A feature with an empty name is returned if there\s no feature at the given
// position.
rpc GetFeature(Point) returns (Feature) {}
// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
// A latitude-longitude rectangle, represented as two diagonally opposite
// points lo and hi.
message Rectangle {
// One corner of the rectangle.
Point lo = 1;
// The other corner of the rectangle.
Point hi = 2;
}
// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
message Feature {
// The name of the feature.
string name = 1;
// The point where the feature is detected.
Point location = 2;
}
// A RouteNote is a message sent while at a given point.
message RouteNote {
// The location from which the message is sent.
Point location = 1;
// The message to be sent.
string message = 2;
}
// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
message RouteSummary {
// The number of points received.
int32 point_count = 1;
// The number of known features passed while traversing the route.
int32 feature_count = 2;
// The distance covered in metres.
int32 distance = 3;
// The duration of the traversal in seconds.
int32 elapsed_time = 4;
}