gRPC实践

 简介
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

优点

  1. 基于 HTTP2 之上的二进制协议(Protobuf 序列化机制);
  2. 一个连接上可以多路复用,并发处理多个请求和响应;
  3. 多种语言的类库实现;
  4. 服务定义文件和自动代码生成(.proto 文件和 Protobuf 编译工具)。
  5. RPC 还提供了很多扩展点,用于对框架进行功能定制和扩展,例如,通过开放负载均衡接口可以无缝的与第三方组件进行集成对接(Zookeeper、域名解析服务、SLB 服务等)
  6. 效率高于 restful 服务,编码节约空间,使得在低带宽场景下很有优势
  7. protobuf通过编译工具生成数据读写代码,提高开发者编码效率。
  8. 支持向上游传递超时时间,让上游在发现超时时主动决定如何执行后续操作,http1.1则会直接断开连接。

缺点

  1. 1只有少数浏览器支持gRPC。
  2. 移动端网络可能在wifi及4G频繁切换,无法体现出长连接以及多路复用的优势。
  3. 编码后数据可读性低,json则具有很高的可读性。

gRPC 是什么?

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。

调用过程

RPC 远程过程调用是指计算机 A 上的进程,调用另外一台计算机 B 上的进程的方法。其中A 上面的调用进程被挂起,而 B 上面的被调用进程开始执行对应方法,并将结果返回给 A,计算机 A 接收到返回值后,调用进程继续执行。
发起 RPC 的进程通过参数等方式将信息传送给被调用方,然后被调用方处理结束后,再通过返回值将信息传递给调用方。这一过程对于开发人员来说是透明的,开发人员一般也无须知道双方底层是如何进行消息通信和信息传递的,这样可以让业务开发人员更专注于业务开发,而非底层细节。
RPC 让程序之间的远程过程调用具有与本地调用类似的形式。比如说某个程序需要读取某个文件的数据,开发人员会在代码中执行 read 系统调用来获取数据。
当 read 实际是本地调用时,read 函数由链接器从依赖库中提取出来,接着链接器会将它链接到该程序中。虽然 read 中执行了特殊的系统调用,但它本身依然是通过将参数压入堆栈的常规方式调用的,调用方并不知道 read 函数的具体实现和行为。
当 read 实际是一个远程过程时(比如调用远程文件服务器提供的方法),调用方程序中需要引入 read 的接口定义,称为客户端存根(client-stub)。远程过程 read 的客户端存根与本地方法的 read 函数类似,都执行了本地函数调用。不同的是它底层实现上不是进行操作系统调用读取本地文件来提供数据,而是将参数打包成网络消息,并将此网络消息发送到远程服务器,交由远程服务执行对应的方法,在发送完调用请求后,客户端存根随即阻塞,直到收到服务器发回的响应消息为止。
 下图展示了远程方法调用过程中的客户端和服务端各个阶段的操作。
当客户端发送请求的网络消息到达服务器时,服务器上的网络服务将其传递给服务器存根(server-stub)。服务器存根与客户端存根一一对应,是远程方法在服务端的体现,用来将网络请求传递来的数据转换为本地过程调用。服务器存根一般处于阻塞状态,等待消息输入。
 当服务器存根收到网络消息后,服务器将方法参数从网络消息中提取出来,然后以常规方式调用服务器上对应的实现过程。从实现过程角度看,就好像是由客户端直接调用一样,参数和返回地址都位于调用堆栈中,一切都很正常。实现过程执行完相应的操作,随后用得到的结果设置到堆栈中的返回值,并根据返回地址执行方法结束操作。以 read 为例,实现过程读取本地文件数据后,将其填充到 read 函数返回值所指向的缓冲区。
  read 过程调用完后,实现过程将控制权转移给服务器存根,它将结果(缓冲区的数据)打包为网络消息,最后通过网络响应将结果返回给客户端。网络响应发送结束后,服务器存根会再次进入阻塞状态,等待下一个输入的请求。
  客户端接收到网络消息后,客户操作系统会将该消息转发给对应的客户端存根,随后解除对客户进程的阻塞。客户端存根从阻塞状态恢复过来,将接收到的网络消息转换为调用结果,并将结果复制到客户端调用堆栈的返回结果中。当调用者在远程方法调用 read 执行完毕后重新获得控制权时,它唯一知道的是 read 返回值已经包含了所需的数据,但并不知道该 read 操作到底是在本地操作系统读取的文件数据,还是通过远程过程调用远端服务读取文件数据。
总结下RPC执行步骤:
  1. 调用客户端句柄,执行传递参数。
  2. 调用本地系统内核发送网络消息。
  3. 消息传递到远程主机,就是被调用的服务端。
  4. 服务端句柄得到消息并解析消息。
  5. 服务端执行被调用方法,并将执行完毕的结果返回给服务器句柄。
  6. 服务器句柄返回结果,并调用远程系统内核。
  7. 消息经过网络传递给客户端。
  8. 客户端接受数据。

使用原理

服务定义从.proto开始,gRPC 提供生成客户端和服务器端代码的插件。gRPC 用户通常在客户端调用这些 API,并在服务器端实现相应的 API。
在服务器方面,服务器实现服务声明的方法,并运行 gRPC 服务器来处理客户端请求。gRPC 基础框架解码传入的请求、执行服务方法并编码服务响应。
在客户端方面,客户端有一个称为stub的本地对象(对于某些语言,首选术语是客户端),该对象实现与服务相同的方法。然后,客户端只需在本地对象上调用这些方法,在适当的protbuf消息类型中包装请求的参数 - gRPC 在将请求发送到服务器并返回服务器的协议缓冲响应后进行处理。

服务定义

gRPC 允许您定义四种服务方法:
  1. 客户端向服务器发送单个请求并得到单个响应的单个 RPC,就像正常功能呼叫一样
rpc GetHello(HelloRequest) returns (HelloResponse);
  1. 服务器流 RPC,其中客户端向服务器发送请求并获取流以读回一系列消息。客户端从返回的流中读取,直到没有更多消息
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  1. 客户流 RPC,其中客户端编写一系列消息并将其发送到服务器,再次使用提供的流。一旦客户端写完消息,它就会等待服务器读取并返回其响应
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  1. 双向流式流式传输 RPC,其中双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以随心所欲地读写:例如,服务器可以等待接收所有客户端消息后再编写其回复,或者可以交替读取消息,然后编写消息,或者将读写与其他组合。每个流中的信息都是有顺序的。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

同步与异步

gprc支持同步和异步调用。同步方式下 RPC发出请求,阻塞直到从服务器获取响应,这种是最接近程序调用的。另一方面,网络本质上是异步的,在许多情况下,启动 RPC后,可能并不需要阻塞当前线程。

rpc的种类

一元 RPC

首先考虑最简单的 RPC 类型,其中客户端发送单个请求并返回单个响应。
一旦客户端调用存根方法,服务器就会收到通知,该 RPC 已与客户端的元数据一起调用,该调用、方法名称以及指定的截止日期(如果适用)。
然后,服务器可以立即发回自己的初始元数据(必须在任何响应之前发送),也可以等待客户端的请求消息。首先发生的是应用程序特定。
一旦服务器有客户端的请求消息,它就会做任何必要的工作来创建和填充响应。然后将响应(如果成功)与状态详细信息(状态代码和可选状态消息)以及可选尾随元数据一起返回给客户端。
如果响应状态正常,则客户端将获得响应,从而完成客户端上的呼叫。

服务器流 RPC

服务器流 RPC 类似于单一 RPC,但服务器会响应客户的要求返回消息流。发送所有消息后,服务器的状态详细信息(状态代码和可选状态消息)和可选尾随元数据将发送到客户端。这将完成服务器端的处理。客户端一旦收到服务器的所有消息,就会完成。

客户流 RPC

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

双向流 RPC

在双向流式流式传输 RPC 中,呼叫由客户端调用方法和接收客户端元数据、方法名称和截止日期的服务器启动。服务器可以选择发回其初始元数据或等待客户端开始流式传输消息。
客户端和服务器端流处理是特定于应用程序的。由于两个流是独立的,客户端和服务器可以按任何顺序读取和写邮件。例如,服务器可以等到收到客户端的所有消息后再编写消息,或者服务器和客户端可以播放"ping-ping"- 服务器收到请求,然后发回回复,然后客户端根据响应发送另一个请求,等等。

操作

RPC超时

gRPC 允许客户指定他们愿意等待 RPC 完成多长时间,然后再以错误终止 RPC。在server端,可以查询特定 RPC 是否已过时,或者需要多少时间才能完成 RPC。
指定截止日期或超时是特定于语言的:某些语言 API 以超时(时间期限)为工作,某些语言 API 以截止日期(固定时间点)工作,并且可能具有默认截止日期,也可能没有默认截止日期。

RPC 终止

在 gRPC 中,客户端和服务器都对调用的成功做出独立和本地的判断,它们的结论可能不一致。 这意味着,例如,您可能有一个 RPC 在服务器端成功完成(“我已经发送了我所有的响应!”)但在客户端失败(“响应到达前我已经截至了!”)。 服务器也可以决定在客户端还没发送完所有请求时截至请求。

RPC取消

客户端或服务器可以随时取消 RPC。RPC会立即响应取消操作而终止,以便不再进行进一步的工作。

gRPC服务注册

注册到nacos:
注册中心对比:https://blog.csdn.net/qq_42046105/article/details/123436832

使用例子

搭建例子工程

gRPC的依赖配置

gRPC的编译插件

proto文件

 

编译proto文件

服务端--依赖配置

服务端--配置文件

服务端--RPC服务实现

服务端--RPC服务启动

服务注册到nacos

客户端--依赖配置

客户端--配置文件

客户端--调用RPC服务

客户端-启动

测试访问RPC服务

再看看nacos服务列表

源码文件

 https://files.cnblogs.com/files/lxiaojun/grpc-springboot-demo.zip?t=1652195450

posted @ 2022-05-10 23:10  刻思行  阅读(246)  评论(0)    收藏  举报