此文翻译的原文地址:gRPC Concepts
综述
服务定义
和许多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 RPC:客户端向服务器端发送请求,并得到响应,类似于方法调用:
rpc SayHello(HelloRequest) returns (HelloResponse);
- Server streaming RPC:客户端可以向服务器发送请求,获取服务端返回的流响应,客户端可从流中读取一组消息。客户端可以持续读取消息直至消息全部读取完成。gRPC保证消息顺序的正确性:
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
- Client streaming RPC:客户端会写入一组消息,然后基于流的方式发送给服务端。当客户端写完全部消息后,就等待服务端进行消息的读取并等待服务端响应。gRPC保证消息顺序的正确性:
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
- Bidirectional streaming RPC:服务端和客户端都可以使用读写流发送一组消息。服务端的流和客户端的流是相互独立的,所以服务端和客户端可以按照自己的方式进行流的写入和读取。如:服务端可以决定在全部接收完客户端发送的消息后再进行响应,或者它可以读取一条消息,就写入一条消息,或者其他的一些读写组合。同样的在流中的消息的顺序是可以保证的。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
使用API
在我们在一个.proto文件中定义完服务后,gRPC提供protocol buffers编译器插件来产生客户端和服务端代码。gRPC通常是在客户端进行调用,在服务端实现服务定义的方法:
- 在服务端,会实现服务中声明的方法,然后启动一个gRPC服务器用于响应客户端的请求。gRPC底层逻辑会正确的把接收到的请求信息转化为对应的参数类型、进行方法调用,然后返回响应;
- 在客户端,有一个本地对象实现了服务的同样的方法。客户端可以直接调用本地对象上的方法,gRPC会自动的发送请求,并正确的得到响应;
同步和异步
同步调用会阻塞客户端代码执行,知道从服务端得到响应。这种方式是和RPC想要达到的模拟本地调用的目的最接近的一种方式。但是在另一方面,网络本质上就是异步的,而且在一些场景下,不阻塞客户端代码执行也会非常有用处。
gRPC在大多数的语言中都支持同步和异步的调用。
RPC生命周期
在这一部分我们会详细看看在一个gRPC客户端调用服务端方法时发生了哪些事情
Unary RPC
首先让我们看看最简单的RPC调用方式 – 客户端发送一个请求并获取一个响应。
当客户端调用本地的桩方法时,服务端会得到一个RPC被调用的通知,通知中包含了关于此次调用的元数据信息:方法名、指定的合适的超时时间。
服务端可以立即返回一些关于它自己的初始化元数据(必须在响应发送前发送),或者等待客户端的请求信息,当然这两种方式是和具体的应用相关的。
当服务端接收到客户端的请求信息后,它会执行具体的逻辑以便产生一个响应。响应会和一个描述状态的详细信息(状态码和可选的状态信息)及一个可选的附属的元数据一起发送给客户端。
如果响应的状态是OK,则客户端就得到了响应,完成了一次RPC的调用。
Server streaming RPC
server-streaming RPC和unary RPC类似,不同点在于服务端不再返回一个简单的响应,而是返回一个信息流。在信息流发送完成后,服务端的描述状态的详细信息(状态码和可选的状态信息)及一个可选的附属的元数据就会发送给客户端。这样就完成了服务端的处理;当客户端获取了所有的消息后,就完成此次的调用全过程。
Client streaming RPC
client streaming RPC和unary RPC类似,不同点在于客户端不是发送一个简单的请求而是一个消息流。服务端会返回一个响应(包括了处理状态的详细信息和一个可选的附属的元数据),这个响应可在服务端接收完全部消息后进行返回,也可以在接收的过程中就进行返回。
Bidirectional streaming RPC
在bidirectional streaming RPC调用过程中,调用是由客户端发起的,服务端会得到客户端的元数据、调用的方法名和超时时间。服务端可以选择立即向客户端发送初始化元数据或等待客户端发送消息流。
客户端和服务端的流的处理方式都是和应用相关的。因为客户端的流和服务端的流是独立的,所以客户端和服务端可以以不同的顺序进行消息的读写。
Deadlines/Timeouts
gRPC允许客户端指定在RPC通过DEADLINE_EXCEEDED错误终止前希望等待的时间。在服务端可以查看一个特定的RPC是否已经超时了,或者在完成一次RPC前还剩下多少时间。
使用deadline还是timeout进行超时指定是由特定的语言自己决定的:一些语言使用timeout(调用可持续的时间段),一些语言使用deadline(固定的时间点),是否有缺省值也是由具体的语言决定的。
RPC termination
gRPC中,客户端和服务端对于调用是否成功的判定是相互独立的,而且他们的结论也可能是不同的。这意味着在服务端一个RPC调用已经正常完成了(发送了所有的消息),但是在客户端却失败了(消息是超时后才收到的)。另外在客户端发送完所有的消息前,服务端就可能正常的结束一个调用。
Cancelling an RPC
服务端和客户端都可以在一个RPC调用过程中取消调用。取消会立即终止RPC调用,以防止产生无效的其他消耗。但是这种取消,不会回滚所有已经作出的变更。
Metadata
Metadata是以key-value形式描述一次特定的RPC调用(如:认证详细信息)的信息。 其中key一定是一个字符串,而value通常是字符串,但也可以是二进制数据。Metadata对于gRPC来说比较难理解:它让客户端提供关于一次调用的相关信息,反之亦然。
对于Metadata的访问,是由各个实现语言自己控制的。
Channels
gRPC channel提供了一个特定gRPC服务端的连接。它用于创建客户端桩代码。客户端通过指定channel的参数来改变gRPC的默认行为,如:是否打开压缩消息的能力。Channel是有状态的,如:已经连接或空闲的。
对于如何关闭一个channel,每个语言都有自己不同的方式。有些语言还允许查询channel的状态。