全文参考自:gRPC 官方Docs

概述

gRPC体系

  • 在gRPC体系中,client可以像调用本地方法一样直接调用位于不同主机上的server方法,使得创建分布式应用和服务更加简便。就像许多RPC系统一样,gRPC基于服务定义的思想,定义了可以被远程调用的方法、参数及其返回类型。
  • 在server端,server运行gRPC server来处理client请求;在service端,client运行gRPC stub来提供server的相同方法。
    gRPC的体系结构示意图如下所示:

gRPC的体系结构示意图

  • gRPC
    client和server可以在不同环境中独立运行、互相通信,并且可以以任何一个gRPC所支持的语言(如Go、Python、Ruby等)实现。除此之外,最新的GoogleAPIs也会有对应的gRPC版本,可以轻松在应用中使用Google功能。

Protocol Buffers

  • gRPC默认使用Protocol Buffers(proto3)进行数据传输。
  • 使用Protocol Buffers的第一步是定义数据的结构,使其能够序列化为.proto文件。结构化后的数据被称为消息(message)
    ,每一个message是一个小型逻辑信息记录,包含了一系列的<name, value>对,称为**域(fields)**。该结构举例如下:
1
2
3
4
5
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
  • 当结构定义完成后,可以使用protocol buffer编译器protoc去生成proto中指定语言的数据获取类(data access class),该类为每个field提供了基础方法,如name()set_name() ,还有将整个结构与字节流互相转化的方法。例如,如果选择的目标语言是C++,那么运行编译器后将会生成一个名叫Person的类,可以在应用程序中使用、序列化、检索此类的Protocol Buffer messages。

  • 使用gRPC定义服务的举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Greeter服务定义
service Greeter {
// 使用RPC发送greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 请求message定义
message HelloRequest {
string name = 1;
}

// 相应message定义
message HelloReply {
string message = 1;
}
  • gRPC根据用户所编写的.proto文件,使用gRPC插件和protoc编译器生成gRPC client和service代码,protocol buffer代码可以被使用、序列化、检索message类型。

核心概念

Service

  • gRPC基于service进行构建,该service中声明了可以被远程调用的方法、参数、返回类型。
  • gRPC默认使用protocol buffers作为接口定义语言(Interface Definition Language, IDL),用来描述service接口和负载message的结构。也可以使用其他结构代替protocol buffers。
  • gRPC service可定义的类型有4种:
  1. Unary RPCs (一元RPCs,1:1)
    • 1 request <=> 1 response
    • client发送一个请求,server返回一个响应(类似于标准函数调用)
1
rpc SayHello(HelloRequest) returns (HelloResponse);
  1. Server streaming RPCs (Server流式RPCs,1:n)
    • 1 request <=> N response
    • client发送一个请求,server返回一序列响应(序列内能够保证读写操作顺序)
1
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  1. Client streaming RPCs (Server流式RPCs,n:1)
    • N request <=> 1 response
    • client发送一序列请求,server待接收到所有request后返回一个响应(序列内能够保证读写操作顺序)
    • client会保持等待
1
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  1. Bidirectional streaming RPCs (双向流式RPCs,n:n)
    • N request <=> N response
    • client发送一序列请求,server返回一序列响应(序列内能够保证读写操作顺序)
    • 两个stream独立运行,可以按任意形式进行读写(server接收部分后返回or接收全部再返回)
1
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

API的使用

  • 如前所述,通过使用.proto文件可以定义service,同时gRPC提供了protocol buffer编译器插件来生成client端和server端的代码。用户需要调用client端的APIs,并实现server端对应的API方法。
  • 在server端,server实现service中声明的方法,并运行gRPC server来处理client的调用请求。
  • 在client端,使用stub本地对象来实现service中同样的方法,这样client就可以仅调用本地对象的这些方法,并以protocol buffer message形式对调用request及其参数进行包装。gRPC将request发送至server,并返回server端的protocol buffer response。

同步(Synchronous) vs 异步(asynchronous)

  • 同步RPC会在得到response前保持阻塞,这是对RPC所希望的过程调用的最接近近似。但另一方面,网络本质上是异步的,在许多场景下都需要RPCs在不阻塞当前线程的情况下启动。
  • 大多数语言中的 gRPC API 都有同步和异步两种形式,可在具体语言中进行使用。

RPC生命周期

Service

Unary RPC

  • 一旦client调用了自身的一个stub方法,server就会收到通知RPC已被调用,通知中包含该调用的client metadata、方法名称和指定的截止时间(如果适用)。
  • 然后,server可以立即发回自己的初始metadata(必须在任何response之前发送),或者等待client的request message。哪个先发生视具体情况而定。
  • 一旦server收到了client的request message,它会执行创建和生成response所需的任何工作。然后将response连同其状态详细信息(状态码和可选状态message)和可选附属metadata一起返回(如果成功)给client。
  • 如果response的返回状态为OK,则client在收到这一response后就会结束这次调用

Server streaming RPC

  • Server streaming RPC与Unary RPC类似,除了server会针对client的一个request返回一序列的message。
  • 当server所有的message都被发送出去后,server的状态详细信息(状态码和可选状态message)和可选附属metadata会被发送给client。
  • server端将会在所有message发送完成后结束;client端将在收到所有server message后结束。

Client streaming RPC

  • streaming RPC与Unary RPC类似,除了client会发送一序列的message给server,而不是单个message。
  • server response的内容是单个message,内容包括:状态详细信息和可选附属metadata会被发送给client,这一response message的发送并不一定要在所有client信息都被接收到后才发送。

Bidirectional streaming RPC

  • 在Bidirectional streaming RPC中,调用由调用方法的client发起,server接收client metadata、方法名称和截止时间。server可以选择发回其初始metadata或等待client开始流式传输message。
  • client和server的流处理是特定于应用程序的。由于这两个流是独立的,因此client和server可以以任意顺序读写消息。
  • 例如,server可以等到它收到client的所有message后再写入它自己的message;或是client和server可以“打乒乓”——server收到request,然后发回response,然后client根据response发送另一个request……依此类推。

截止时间(Deadlines)/超时(Timeouts)

  • gRPC允许client指定他们愿意等待RPC完成的时间,避免RPC因出现DEADLINE_EXCEEDED错误而终止。在server端,server可以查询某个RPC是否已经超时,或是还要多久RPC才可以完成。
  • 声明一个deadline或timeout的方法是语言相关的:一些语言使用timeout概念(表示持续时间);一些语言使用deadline概念(表示一个固定时间点),可能存在默认值。

RPC的终止(termination)

  • 在gRPC中,client和server都能够独立决定某次调用是否成功,这意味着他们可能产生不同的结论。
  • 例如:server认为自己已经发送了全部的response,而client却认为已经超时。还有可能server在client发送全部request前就提前结束了。

取消(Cancel)一个RPC

  • client和server都能够在任意时间取消一个RPC。一次取消意味着RPC被立即终止,不会再执行任何的任务。

注意:在取消前所执行的改变不会被回滚。

元数据(metadata)

  • metadata是一个键值对列表,包含了某一次RPC调用的信息(如权限验证等)。key为string,value可以为string或二进制数据。
  • metadata对于gRPC自身而言是不可见的,它使得client能够提供与调用相关的信息给server,反之亦然。
  • 不同语言对metadata的获取方式是不同的。

通道(Channels)

  • gRPC通道使得gRPC server能够连接到指定的主机地址和端口。当创建client
  • stub时会使用到stub,client可以设定通道参数来修改gRPC的默认行为(如是否开启message压缩)。
  • 通道具有状态性(连接or空闲)。不同语言关闭gRPC通道的方式是不同的,一些语言也会支持通道状态的查询。