前言:
原文链接。日常学习,若有错误,欢迎指正。
一 概览
服务定义
像许多RPC系统,gRPC基于一个基本思想,定义一个服务,明确规定接口的参数和返回值。默认情况下,gRPC使用PB作为它的接口定义语言。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPC可以定义4种类型的服务方法:
- Unary RPCs: 客户端发送一个请求给服务端,服务端返回单个响应,就像普通函数调用那样。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- Server streaming RPCs: 客户端发送一个请求给服务端,并且得到了一个流去读取一系列返回消息。客户端从返回流中读消息直到没有消息可读取。gRPC保证一个RPC调用里的消息顺序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- Client streaming RPCs: 客户端利用提供的流写一系列消息并将它们发送给服务端。一旦客户端完成写消息,它将等待服务端读取消息并回复。同样,gRPC保证一个RPC调用里的消息顺序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- Bidirectional streaming RPCs: 客户端服务端2方利用读写流发送一系列消息。这2个流操作是相互独立的,因此客户端和服务端可以用任何顺序读写:例如服务端可以在写回复之前接收所有的客户端消息;或者它可以读一个请求消息然后写一个回复消息;或者是读和写的其它结合方式 。每个流的消息顺序都是保留的。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
接下来我们将在RPC生命周期中详细查看不同类型的RPC
使用API surface
从 .proto 文件的服务定义开始,gRPC提供了protocol buffer编译器插件来生成客户端和服务端代码。gRPC用户在客户端调用这些API,服务端实现相应的代码。
- 服务端侧,服务实现了服务定义的方法,并且运行一个gRPC服务去处理客户端调用。gRPC架构解码请求,执行服务方法,并且编码服务响应。
- 客户端侧,客户端有一个本地对象stub (一些语言中被称作client)实现了服务的相同的方法。客户端可以仅仅调用本地对象的这些方法,用合适的protocal buffer消息类型封装请求,gRPC向服务端发送这些请求并且返回服务响应。
同步 vs 异步
同步RPC调用会在响应到达前阻塞(这也是RPC抽象概念里最渴望的)。在另一方面,网络本质上是异步的,并且在许多场景中,不阻塞当前线程的RPC是很有用的。
在许多语言中,gRPC编程都是同时支持同步调用和异步调用。你可以在每个语言的指南中找到更多相关信息。
二 RPC生命周期
现在让我们详细看下gRPC客户端调用gRPC服务端方法发生了什么。
Unary RPC
首先让我们看下RPC中最简单的类型:客户端发送单个请求并得到单个响应。
- 一旦客户端调用 stub/client 对象中的方法,服务端就会被通知RPC被调用了,伴随着服务名称和deadline(如果被定义的话)。
- 服务端可以立即发送初始信息内容(必须在任何回复之前发送),或者等待客户端的请求消息。
- 一旦服务接收到了客户端的请求消息,它将做必要的工作去创建和填充回复消息。如果请求处理成功,回复消息以及状态详情和可选的尾部元数据之后会被返回给客户端(包含状态码和可选的状态消息)。
- 如果status是OK, 客户端完成了调用并得到回复。
Server streaming RPC
server-streaming RPC和我们简单的例子是相似的,除了服务端在得到客户端请求消息后会返回一连串回复。在发送完所有的回复消息 ,服务端的状态详情(例如状态码和可选的状态消息)以及可选的尾部元数据后,服务端就完成了它的任务。客户端这里一旦接收到了服务端所有的回复后就完成了调用。
Client streaming RPC
client-streaming RPC和简单例子也是相似的,除了客户端发送给服务端的是一连串请求消息而不是单个请求消息。典型的但不是必须的,服务端在接收到所有的客户端请求后,返回单个回复消息,以及状态详细和可选的尾部元数据。
Bidirectional streaming RPC
在一个bidirectional streaming RPC中,再一次的客户端初始化调用方法,服务端接收到客户端发送的元数据,方法名称和deadline。服务可以选择发送回初始化元数据或者等待客户端开始发送请求。
接下来发生什么依赖于应用程序,客户端和服务端可以用任意顺序读写消息-流操作直接是完全相互独立的。例如,服务端可以在写回复消息之前等待直到它接收到所有的客户端消息,或者服务端和客户端可以 "ping-pong": 服务端获取一个请求,然后发送 一个响应,客户端根据回复发送另一个请求等等 。
Deadlines/Timeouts
gRPC允许客户端去定义它们愿意等一个RPC多久去完成在一个RPC被错误DEADLINE_EXCEEDED终止之前。在服务端,服务可以查询去看是否一个特定的RPC已经超时了,或者还剩多少时间去完成RPC。
dealine和timeout的定义方式不同语言之间是不同的。例如,不是所有的语言都有一个默认的deadline,一些语言的APIs就deadline(一个固定的时间点)工作,一些语言的APIs就timeouts工作(时间区间)。
RPC termination
在gRPC中,客户端和服务端有独立的和本地的确定关于调用是否成功,并且它们的结论不一定是一致的。这意味着,你可以有一个RPC在服务端成功完成了(我已经成功发送了我所有的回复),但是在客户端失败了(回复在我的deadline之后到达了)。这也是有可能的一个服务在客户端发送完它所有的请求之前决定完成RPC调用 。
Cancelling RPCs
客户端和服务端都可以在任意时间取消RPC调用。一个取消立即终止RPC调用,以致不再有工作继续执行。它不是一个"undo": 在取消前执行的工作不会被回滚。
Metadata
Metadata关于一个特定RPC调用的信息(例如授权详情),它的形式是一列key-value键值对(key是字符串,value通常是字符串,当然也可以是二进制数据)。元数据对gRPC本身是不透明的-它在调用中让客户端提供信息给服务端,反之也是一样。
访问元数据是语言相关的。
Channels
一个gRPC channel提供了一个向特定主机和端口服务的连接,并且被使用当创建了一个client stub。客户端可以定义channel参数去调整gRPC的默认 行为,例如开或者关消息压缩。一个channel有状态,包括connected和idle。
gRPC是如何处理关闭的channel是语言相关的。一些语言也禁止查询channel状态 。
网友评论