最近项目需要用到 gRPC,网上gRPC 的资料较少,翻译了官网的 gRPC guide 文档,以供组内学习,部分暂时用不到的部分未进行翻译。
gRPC Guide
该文档主要介绍 gRPC 和 protocol buffers。 gRPC 使用 protocol buffers 作为 IDL 和消息交换格式。本文档适合与刚接触 gRPC 或者 protocol buffers 的初学者。
概述
gRPC client 可以直接像调用本地方法一样调用 server 的方法,这使得开发分布式应用和服务变得更加简单。和很多其他 RPC 框架一样,gRPC 通过指定方法调用的参数和返回值来定义服务。server 实现 RPC 接口,处理客户端的调用请求。client 提供和 server 一样的接口。
gRPC client 和 server 可以在不同的环境运行和通信,比如 Google 内部的服务器到你的桌面应用。client 和 server 的语言可以是任何 gRPC 支持的语言,你可以用 C++ 来实现 server, 用 java 和 ruby 来实现 client。另外最新的 Google API 都提供了 gRPC 接口,来帮助你在应用中更便捷的使用 Google 提供的功能。
Working with Protocl Buffers
gRPC 默认使用 Protocol buffers,这是一个 google 开源的成熟的数据序列化机制。接下来会简单介绍一下它是如何工作的。
使用 protocol buffers 前需要先将你要序列化的结构化数据定义为 .proto 文件。protocol buffers 通过 messages
定义数据结构,每个 message
由一系列属性组成,类似于类的定义,我们可以看一个简单的例子
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
定义好数据结构后,可以使用 protocol buffers 编译器(protoc)将其生成其他语言的类。这些类提供所有属性的访问方法,及序列化和反序列方法。例如,你使用的是 C++,运行编译器会生成一个 Person 类,你可以在在你的应用中实例化或序列化来传输这些 protocol buffers 信息。
除了数据结构外,你还可以在 .proto 文件中定义 gRPC 服务,包括服务接口的参数和返回类型。例如:
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
和正常使用 protocol buffers 不一样, gRPC 可以使用 protoc 的 gRPC 插件生成代码,除了会生成共传播和序列化的类以外,还会生成 gRPC client 和 server 代码。
protocol buffers 版本
建议使用 proto3 版本的 protocol buffers,语法更简洁,支持更多语言。
基本概念
服务定义
之前已经提到了,gRPC 的基于 protocol buffers 定义服务。所谓定义服务,就是使用 IDL 来描述你的服务接口和传输消息结构。gRPC 使用 protocol buffers 作为 IDL。
gRPC 支持四种类型的服务方法
- 简单RPC,client 发送单个请求,server 返回单个响应,和普通的方法调用一样。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- server 流式RPC, client 发送单个简单请求,server 返回流,用于读取一系列的消息。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- client 流式 RPC, client 通过流向 server 发送一些列的消息,当 client 发送完消息后,等待服务端读取并返回响应
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 双向的流式 RPC,client 的请求和 server 的响应都是流式的。注意这两条流是独立的,所以 server 可以边读请求边返回响应,也可以将流读完在返回响应。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
API 使用
gRPC 使用 .proto
生成 client 和 server 代码及 API。client 调用这些 API, server 实现这些API。
- server 侧实现服务声明的方法,并运行 gRPC server 来处理 client 的调用。gRPC 帮助你反序列化请求,执行服务方法,序列化响应并返回给 client.
- client 侧 gRPC 会生成一个 client,这个 client 提供和 server 侧一样的接口供用户调用,并且使用 protocol buffer 序列化用户的请求参数,发送给 server,然后反序列化 server 的响应。
同步 VS 异步
同步 RPC 调用在 server 的响应到达之前会阻塞,这是 RPC 的常用使用方式。另一方面,web 服务在很多场景下天然是异步的,因此异步的 RPC 也经常使用,异步 RPC 调用不会阻塞当前线程。
gRPC 的同步和异步调用完全依赖用户自身的代码处理。
RPC life cycle
简单 RPC
- 当 client 调用 RPC 方法时,会通知 server 对应的 RPC 方法被调用,包括 client 调用的元数据信息,方法名,以及 deadline
- server 也需要通知 client 自己的元数据(这步发生在所有响应之前),可以直接通知,也可以等收到客户端第一个请求时发送
- 当 server 接到 client 的请求后,根据请求和业务逻辑生成响应,这些响应包括状态码及payload,并返回给 client
- 如果状态 OK, client 会收到响应,并在 client 侧完成这次调用
流式 RPC
Deadline / timeout
gRPC 允许 client 设置过期时间,超过过期时间 RPC 会被中止,并返回 DEADLINE_EXCEEDED 错误。。同时 server 可以查看某个 RPC 是否超时,或者还有多长时间超时。
deadline 或者 timeout 在不同语言的 API 可能不一样,不一定都提供, deadline = timeout + now。
RPC 中断
在 gRPC 中,client 和 server 都可以独自决定一次调用是否成功,并且结论可能不一致。这也就意味,server 可能认为 RPC 已经成功了,但是 client 认为失败了(比如超时)。甚至 server 可以在 client 还未将 request 完全发送完就结束一次调用。
Metadata
Metadata 元数据使用 kv 对表示某个特定 RPC 调用的信息,key 是字符串,values 通常也是字符串,也可以是二进制数据。元数据对于 gRPC 本身是不透明的,它提供了使 client 与 server 端调用关联起来的信息。
通道 (Channels)
创建 client 时,gRPC 的通道提供了和某个特定 host 及 port 的 gRPC server 之间的连接。clietn 可以通过参数改变 gRPC 的默认欣慰,比如消息压缩开关等。通道是有状态的,包括 connected
和 idle
。
如何处理关闭的通道不同语言不太一样。有的语言甚至不提供通道状态查询。
鉴权
可通过 TSL 或 Token 的方式进行鉴权。
HTTP2
gRPC 是基于 HTTP2
Request:
HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
DATA (flags = END_STREAM)
<Delimited Message>
Response
HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
DATA
<Delimited Message>
HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc
错误处理
错误模型
我们之前看到的信息都是 server 返回状态正常 OK 时的情况,如果调用不成功会发生什么呢?
当错误发生时,gRPC 会返回状态码,也可能会包括一些错误的描述信息。
错误状态码
gRPC 会在很多情况下产生错误,包括网络失败,鉴权失败等,这些错误都会伴随一个状态码,所有语言的状态码都是统一的。
通用错误
描述 | 状态码 |
---|---|
client 取消请求 | GRPC_STATUS_CANCELLED |
超时 | GRPC_STATUS_DEADLINE_EXCEEDED |
server 端未找到对应方法 | GRPC_STATUS_UNIMPLEMENTED |
server 已关闭 | GRPC_STATUS_UNAVAILABLE |
server 内部错误 | GRPC_STATUS_UNKNOWN |
网络错误
描述 | 状态码 |
---|---|
在deadline 之前没有数据传回 | GRPC_STATUS_DEADLINE_EXCEEDED |
在连接破坏前,有一部分数据传回 | GRPC_STATUS_UNAVAILABLE |
协议错误
描述 | 状态码 |
---|---|
压缩算法不支持 | GRPC_STATUS_INTERNAL |
client 使用的压缩机制 server 不支持 | GRPC_STATUS_UNIMPLEMENTED |
流量超过限制 | GRPC_STATUS_RESOURCE_EXHAUSTED |
违反流量控制协议 | GRPC_STATUS_INTERNAL |
未知的返回状态 | GRPC_STATUS_UNKNOWN |
未授权 | GRPC_STATUS_UNAUTHENTICATED |
protocol buffer 解析失败 | GRPC_STATUS_INTERNAL |