本文通过gRPC的结构概述和生命周期介绍一些gRPC理念的关键点。
概述
服务定义
就像很多RPC系统一样,gRPC也是基于定义一个服务的想法,指定可以被远程调用的方法。默认地,gRPC使用protocol buffers作为接口定义语言 (IDL)来描述服务接口和预加载信息的结构。也可能使用其他替代。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPC可以定义四种服务方法:
- Unary RPCs。比如客户端向服务端发送一个请求,然后得到一个返回,就像普通的方法调用:
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- Server streaming RPCs,比如客户端发送请求到服务端,然后得到一个stream读取按序返回的messages。客户端从返回的stream里读取直到没有消息。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- Client streaming RPCs,比如客户端写入一个序列的消息,然后发送到服务端,然后继续使用提供的stream。当客户端结束写入消息后,就会等待服务端读取后返回数据。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 双向streaming RPCs,客户端和服务端都发送信息并且读-写流。两个streams独立操作,所以客户端和服务端可以按照他们想要的读取和写入:比如,服务端可以接收所有的客户端消息再返回数据或者可以先读取然后写数据,或者其他的读写组合方式,每个stream的消息顺序都已经保存了。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
我们在接下来的章节里查看这些不同类型的RPC的详情。
使用API层
从定义服务的.proto文件开始,gRPC提供protocol buffer编译插件生成客户端和服务端的代码。gRPC用户在客户端调用这些APIs,在服务端实现相应的API。
- 在服务端,服务器实现通过service定义的方法,并且运行一个gRPC服务来处理客户端调用。gRPC设施解码请求,执行service方法,然后encode service response.
- 在客户端,客户端有一个已知的本地对象存根(stub)(对于一些语言,首选项是客户端)实现和服务端相同的方法。客户端就可以在本地对象上调用这些方法,为调用在合适的protocol buffer message类型包装参数 - gRPC随后发送请求到服务端然后返回服务端的protocol buffer response(s)。
异步 vs. 同步
同步RPC调用会阻塞,直到服务端有返回是最接近RPC的请求过程的抽象。另一方面,网络本质上是异步的,并且在很多场景,它对在当前线程启动无阻塞的RPCs很有用。
gRPC编程在大多数语言里都有同步和异步。可以在每个语言里的指南里和相关文档查看更多。
RPC生命周期
现在我们来看看当gRPC客户端调用gRPC服务单方法是发生了什么。我们不会看实现细节,你可以在特定语言页里查看更多。
Unary RPC
首先我们来看最简单的RPC类型,客户端发送一个请求并得到一个回复。
- 一旦客户端调用方法在stub/client对象上,服务端会被通知到RPC被调用带着客户端的metadata, 方法名称,特定的deadline如果匹配.
- 服务端然后可以直接返回它的最初的metadata(必须在返回前发送),或者等待客户端的请求信息 - 一个首先发生的application-specific。
- 一旦服务端有客户端的请求信息,创建和填充它的response是必须的。response然后被返回(如果成功)到客户端带着状态详情(状态码和可选的状态信息)和可选的trailing metadata.
- 如果状态是OK的,客户端会得到响应,客户端的请求就完成了。
Server streaming RPC
server-streaming RPC和我们简单的例子相似,除了服务端发送返回一连串的响应在得到客户端的请求信息后。在发送返回了所有的响应后,服务端的状态详情(状态码和可选的状态信息)和可选的trailing metadata会被发送回完成在客户端。客户端在得到所有客户端的相应后完成。
Client streaming RPC
client-streaming RPC也和简单的例子相同,除了客户端发送了一系列的请求到客户端而不是单个请求。服务端返回单个对象,通常但不一定收到了所有客户的请求后,连同其状态的细节和可选的元数据。
Bidirectional streaming RPC
在双向流的RPC里,还是客户端调用方法然后服务端接收客户端metaata,方法名称和deadline。然后服务端可以选择返回最初的metadata或者等待客户端开始发送请求。
接下来的情况就取决于应用了,客户端和服务端都能在任何顺序里读和写。流操作完全是独立的。所以,比如,服务端在写回响应时可以等待直到获取到所有客户端的信息,或者服务端和客户端可以像“乒乓”一样:服务端获取请求,然后返回响应,然后客户端基于返回发送另一个请求,等等。
Deadlines/Timeouts
gRPC允许客户端为一个RPC完成指定超时时间,不然RPC会被终止,抛出DEADLINE_EXCEEDED错误。在服务端,可以查看特定的RPC是否超时,或者还剩下多少时间完成RPC。
如何设置deadline或者timeout取决与不同的语言,比如,不是所有的语言有默认的deadline,有些语言APIs依据deadline(距离上次时间的点),一些依据timeout(间隔)。
RPC termination
在gRPC里,客户端和服务端是独立的,并且本地决定请求的成功,但是结论可能不匹配。这意味,比如,你可能有一个RPC在服务端成功的完成了("我已经发送了所有的响应")但是客户端失败了(“返回在我的deadline之后”)。当然服务端也可能在客户端发送所有请求前完成。
Cancelling RPCs
不管是客户端还是服务端都可以随时取消RPC。取消会立即终止RPC,首页不会再工作。这不是“撤销”: 在取消前已经发生的改变不会被回滚。
Metadata
Metadata是特定RPC请求的信息(比如authentication details)以列表信息的键值对,键是strings,values通常也是strings(但也可以是binary data)。Metadata对于gRPC是透明的 - 它可以让客户端提供调用信息到服务器,或者相反。访问metadata取决于语言。
Channels
gRPC channels在指定的host和port提供连接到gRPC,在创建客户端存根的时候使用。客户端可以指定特定的channel参数修改gRPC的默认行为,比如切换开和关消息压缩。channel有状态,包含connected和idle。
gRPC如何处理关闭channel看编程语言。一些语言也允许查询channel状态。