目录

前言

一、gRPC是什么?

gRPC的特性

gRPC官方支持的语言

二、gRPC和Protocol Buffers介绍

三、核心概念、架构和生命周期

Service definition

2. Api 的使用

3. Synchronous vs. asynchronous

4.RPC 的生命周期

4.1 一元RPC

4.2 服务端流式RPC

4.3 客户端流式RPC

4.4 双向流式RPC

4.5 Deadlines/Timeouts

4.6 RPC 终止

4.7 取消一个RPC

4.8 Metadata

4.9 Channels


前言

在跨进程或者跨服务调用的场景中,例如微服务,Service Mesh,需要在一个服务中调用另外一个服务,即RPC(Remote Procedure Call),语义上理解即将远程服务封装在本地,让调用者像调用本地方法一样调用远程方法。 RPC 使用场景中有明确的服务调用方和服务提供方的角色关系,

        

比如比较流行的实现,Spring Cloud Feign 使用 http 协议实现,Alibaba dubbo, 本篇主要涉及的GRPC也是一种实现。 

一、gRPC是什么?

 github:https://github.com/grpc/grpc

官网:gRPC

gRPC是一个现代的开源高性能远程过程调用(RPC)框架,可以在任何环境中运行。它可以通过可插拔的负载平衡、跟踪、健康检查和身份验证支持,有效地连接数据中心内和数据中心之间的服务。它也适用于分布式计算的最后一英里,将设备、移动应用程序和浏览器连接到后端服务。

gRPC的特性

  1. 简单的服务定义:使用Protocol Buffers 定义服务,Protocol Buffers 是一个高效的,基于二进制序列化的工具集和语言

  2. 快速启动并扩展:只需一行代码即可安装运行时和开发环境,并可通过该框架扩展到每秒数百万个RPC

  3. 跨语言和平台工作:自动为您的服务生成各种语言和平台的惯用客户端和服务器存根

  4. 双向流媒体和集成身份验证:双向流和完全集成的可插拔身份验证与基于HTTP/2的传输

gRPC官方支持的语言

二、gRPC和Protocol Buffers介绍

在gRPC中,客户端应用程序可以直接调用另一台机器上的服务器应用程序上的方法,就像它是本地对象一样,这使您更容易创建分布式应用程序和服务。与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。在服务器端,服务器实现该接口并运行gRPC服务器来处理客户端调用。在客户端,客户端有一个存根(在某些语言中仅称为客户端),它提供与服务器相同的方法。

gRPC客户端和服务器可以在各种环境中运行并相互对话,从谷歌内部的服务器到您自己的桌面,并且可以用gRPC支持的任何语言编写。例如,您可以轻松地用Java创建一个gRPC服务器,客户端使用Go、Python或Ruby。此外,最新的谷歌API将有gRPC版本的界面,让你可以轻松地将谷歌功能构建到你的应用程序中。

默认情况下,gRPC使用 Protocol Buffers,这是谷歌用于序列化结构化数据的成熟开源机制。同样gRPC也可以使用json 序列化协议。

使用Protocol Buffers时,第一步是要在proto格式的文件中定义序列化的数据的结构,这个文件的后缀为 .proto  。Protocol Buffers 数据是一个结构化的message,每个 message 包含了一系列有名称和值组成的字段信息的记录,比如

message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}

然后,一旦指定了数据结构,就可以使用Protocol Buffers 编译器protoc从proto定义中生成指定的语言的数据访问类。它们为每个字段提供了简单的访问器,如name()和set_name(),以及将整个结构序列化/解析为原始字节或从原始字节中序列化/解析的方法。例如,如果您选择的语言是C++,那么在上面的示例中运行编译器将生成一个名为Person的类。然后,您可以在应用程序中使用此类来填充、序列化和检索个人协议缓冲区消息。

您可以在普通proto文件中定义gRPC服务,并将RPC方法参数和返回类型指定为Protocol Buffers messages:

// 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;
}

gRPC使用protoc和一个特殊的gRPC插件从您的proto文件生成代码:您得到生成的gRPC客户端和服务器代码,以及用于填充、序列化和检索消息类型的常规Protocol Buffers 代码。

了解有关协议缓冲区的更多信息,包括如何用所选语言安装带有gRPC插件的protoc protocol-buffers

Protocol Buffers  目前流行版本主要为 proto2,和proto3 ,区别参考文档:寂寞是国,我是王

三、核心概念、架构和生命周期

Service definition

与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。默认情况下,gRPC使用Protocol Buffers 作为接口定义语言(IDL Interface Definition Language),用于描述服务接口和有效负载消息的结构。如果需要,可以使用其他替代品。

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

gRPC允许你定义四种类型的服务方案

  1. 一元RPC,客户端向服务器发送一个请求,然后返回一个响应,就像普通的函数调用一样。

rpc SayHello(HelloRequest) returns (HelloResponse);

  1. 服务器流式RPC,其中客户端向服务器发送请求,并获取一个流以读取一系列消息。客户端读取返回的流,直到不再有消息。gRPC保证消息在单个RPC调用中排序。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

  1. 客户端流式RPC,其中客户端写入一系列消息并将其发送到服务器,再次使用提供的流。一旦客户机完成了消息的编写,它就会等待服务器读取消息并返回其响应。同样,gRPC保证了单个RPC调用中的消息顺序。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

  1. 双向流式RPC,其中双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照他们喜欢的任何顺序进行读取和写入:例如,服务器可以在写入其响应之前等待接收所有客户端消息,或者可以交替读取一条消息,然后写入一条消息,或者其他一些读取和写入的组合。保留每个流中消息的顺序。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

2. Api 的使用

从 .proto 文件的服务定义开始。gRPC提供protocol buffer编译器插件,用于生成客户端和服务器端代码。gRPC用户通常在客户端调用这些API,并在服务器端实现相应的API。

  1. 在服务器端,服务器实现服务声明的方法,并运行gRPC服务器来处理客户端调用。gRPC基础设施对传入的请求进行解码,执行服务方法,并对服务响应进行编码。

  2. 在客户端,客户端有一个称为stub的本地对象(对于某些语言,术语是client),该对象实现了与服务相同的方法。然后,客户端可以在本地对象上调用这些方法,将调用的参数包装在适当的协议缓冲区消息类型中,gRPC负责将请求发送到服务器并返回服务器的protocol buffer响应

3. Synchronous vs. asynchronous

在服务器收到响应之前阻塞的同步RPC调用与RPC所期望的过程调用的抽象最接近。另一方面,网络本身是异步的,在许多情况下,能够在不阻塞当前线程的情况下启动RPC是有用的。

 大多数语言中的gRPC编程API都有同步和异步两种风格。您可以在每种语言的教程和参考文档中找到更多信息(完整的参考文档缺失)。

4.RPC 的生命周期

4.1 一元RPC

一元RPC调用是最简单的调用类型,客户端发送一个请求,并且获取一个来自服务端的详情

  1. 一旦客户机调用stub方法,服务器就会收到通知,RPC已被调用,其中包含该调用的客户机元数据、方法名称和指定的截止日期(如果适用)。

  2. 然后,服务器可以直接发回自己的初始元数据(必须在任何响应之前发送),或者等待客户端的请求消息。首先发生的是特定于应用程序的。

  3. 一旦服务器收到客户端的request, 它就会开始执行逻辑和 ,执行创建和填充响应所需的任何工作。然后将响应(成功或者失败)连同状态详细信息(状态代码和可选状态消息)和可选metadata一起返回给客户端。

  4. 如果响应状态为OK,则客户端将获得响应,从而完成客户端的调用。

4.2 服务端流式RPC

服务器流式RPC类似于一元RPC,不同之处在于服务器返回消息流以响应客户端的请求。发送所有消息后,再将服务器的状态详细信息(状态代码和可选状态消息)和可选的metadata送到客户端。这就完成了服务器端的处理。客户端在拥有服务器的所有消息后完成。

4.3 客户端流式RPC

客户端流式RPC类似于一元RPC,不同之处在于客户端向服务器发送消息流而不是单个消息。服务器通常在接收到所有客户端消息后,以单个消息(及其状态详细信息和可选的尾部元数据)进行响应。

4.4 双向流式RPC

在双向流RPC中,调用由调用方法的客户端和接收客户端metadata、方法名称和截止日期的服务器启动。服务器可以选择发回其初始元数据或等待客户端启动流式消息。

客户端和服务器端流处理是特定于应用程序的。由于这两个流是独立的,因此客户端和服务器可以按任何顺序读取和写入消息。例如,服务器可以等到收到客户端的所有消息后再写入其消息,或者服务器和客户端可以玩“ping-pong”——服务器收到请求,然后发回响应,然后客户端根据响应发送另一个请求,依此类推。

4.5 Deadlines/Timeouts

gRPC允许客户端指定他们愿意等待RPC完成多长时间,并设置超时时间,然后RPC被终止,并出现DEADLINE_EXCEEDED错误。在服务器端,服务器可以查询特定的RPC是否超时,或者还有多少时间来完成RPC。

指定Deadlines或Timeouts是特定于语言的:一些语言API根据超时(持续时间)工作,而一些语言API根据最后期限(固定时间点)工作,或者也可能没有默认的最后期限。

4.6 RPC 终止

在gRPC中,客户端和服务器都对调用的成功进行独立和局部的确定,并且它们的结论可能不匹配。这意味着,例如,您可以有一个在服务器端成功完成的RPC(“我已经发送了所有响应!”)但在客户端失败(“回复在我的截止日期之后到达!”)。服务器也可以在客户端发送所有请求之前决定完成。

4.7 取消一个RPC

客户端或服务器可以随时取消RPC。取消操作会立即终止RPC,以便不再进行进一步的工作。但是取消之前的操作不会被回滚

4.8 Metadata

Metadata关于特定RPC调用的信息(例如身份验证详细信息),其形式为键值对列表,其中键是字符串,值通常是字符串,但可以是二进制数据。元数据对gRPC本身是不透明的-它允许客户端向服务器提供与调用相关的信息,反之亦然。

Metadata的访问取决于语言。

4.9 Channels

gRPC通道提供到指定主机和端口上的gRPC服务器的连接。它在创建客户端存根时使用。客户端可以指定通道参数来修改gRPC的默认行为,例如打开或关闭消息压缩。通道具有状态,包括连接和空闲。

gRPC如何关闭频道取决于语言。一些语言还允许查询通道状态。

参考连接:

Introduction to gRPC | gRPC

https://cloud.google.com/apis/design/proto3?hl=zh_cn

https://developers.google.com/protocol-buffers/docs/proto

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐