社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近项目需要用到 gRPC,网上gRPC 的资料较少,翻译了官网的 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 提供的功能。
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 代码。
建议使用 proto3 版本的 protocol buffers,语法更简洁,支持更多语言。
之前已经提到了,gRPC 的基于 protocol buffers 定义服务。所谓定义服务,就是使用 IDL 来描述你的服务接口和传输消息结构。gRPC 使用 protocol buffers 作为 IDL。
gRPC 支持四种类型的服务方法
rpc SayHello(HelloRequest) returns (HelloResponse){
}
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
gRPC 使用 .proto
生成 client 和 server 代码及 API。client 调用这些 API, server 实现这些API。
同步 RPC 调用在 server 的响应到达之前会阻塞,这是 RPC 的常用使用方式。另一方面,web 服务在很多场景下天然是异步的,因此异步的 RPC 也经常使用,异步 RPC 调用不会阻塞当前线程。
gRPC 的同步和异步调用完全依赖用户自身的代码处理。
gRPC 允许 client 设置过期时间,超过过期时间 RPC 会被中止,并返回 DEADLINE_EXCEEDED 错误。。同时 server 可以查看某个 RPC 是否超时,或者还有多长时间超时。
deadline 或者 timeout 在不同语言的 API 可能不一样,不一定都提供, deadline = timeout + now。
在 gRPC 中,client 和 server 都可以独自决定一次调用是否成功,并且结论可能不一致。这也就意味,server 可能认为 RPC 已经成功了,但是 client 认为失败了(比如超时)。甚至 server 可以在 client 还未将 request 完全发送完就结束一次调用。
Metadata 元数据使用 kv 对表示某个特定 RPC 调用的信息,key 是字符串,values 通常也是字符串,也可以是二进制数据。元数据对于 gRPC 本身是不透明的,它提供了使 client 与 server 端调用关联起来的信息。
创建 client 时,gRPC 的通道提供了和某个特定 host 及 port 的 gRPC server 之间的连接。clietn 可以通过参数改变 gRPC 的默认欣慰,比如消息压缩开关等。通道是有状态的,包括 connected
和 idle
。
如何处理关闭的通道不同语言不太一样。有的语言甚至不提供通道状态查询。
可通过 TSL 或 Token 的方式进行鉴权。
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 |
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!