grpc原理及四种实现方式

1. rpc概述

RPC(Remote Procedure Call)-远程过程调用,他是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC 协议包括序列化、协议编解码器和网络传输栈。
RPC采用客户端/服务器模式。请求程序就是一个客户端,而服务提供程序就是一个服务器。首先,客户端调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端开启进程接收响应信息,获得进程结果,然后继续执行调用进行。

1.1 rpc和http区别

RPC 和 HTTP都是微服务间通信较为常用的方案之一,其实RPC 和 HTTP 并不完全是同一个层次的概念,它们之间还是有所区别的。
  1. RPC 是远程过程调用,其调用协议通常包括序列化协议和传输协议。序列化协议有基于纯文本的 XML 和 JSON、二进制编码的Protobuf和Hessian。传输协议是指其底层网络传输所使用的协议,比如 TCP、HTTP。
 2. 可以看出HTTP是RPC的传输协议的一个可选方案,比如说 gRPC 的网络传输协议就是 HTTP。HTTP 既可以和 RPC 一样作为服务间通信的解决方案,也可以作为 RPC 中通信层的传输协议(此时与之对比的是 TCP 协议)。

2. grpc介绍

gRPC 一开始由 Google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
在 gRPC 中,客户端应用程序可以直接调用不同机器上的服务器应用程序上的方法,就好像它是本地对象一样,从而使您更容易创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 基于定义服务的想法,指定了可以用参数远程调用的方法和返回类型。在服务器方面,服务器实现此接口并运行 gRPC 服务器以处理客户端呼叫。在客户端方面,客户端有一个存根(在某些语言中仅称为客户端),该存根提供与服务器相同的方法。
概念图

gRPC 客户端和服务器可以在各种环境中运行和交互 - 从 Google 内部的服务器到您自己的桌面 - 并且可以用 gRPC 支持的任何语言编写。因此,您可以轻松地在 Java 中创建一个 gRPC 服务器,客户包括 Go、Python 或 Ruby。此外,最新的 Google API 将具有有界面的 gRPC 版本,从而轻松地将 Google 功能构建到您的应用程序中。
gRPC其实就是使用protocol buffer作为序列化协议(编码解码),http2.0作为通讯协议的RPC协议

调用过程

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

2.1. 使用原理

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

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

2.2 服务定义

gRPC 允许您定义四种服务方法:

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

2.3. 同步与异步

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

3. rpc的种类

3.1 一元 RPC

首先考虑最简单的 RPC 类型,其中客户端发送单个请求并返回单个响应。

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

3.2 服务器流 RPC

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

3.3 客户流 RPC

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

3.4 双向流 RPC

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

4. 操作

4.1 RPC超时

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

4.2 RPC 终止

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

4.3 RPC取消

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

4.4 特性

  • 优势
    1.效率高于 restful 服务,编码节约空间,使得在低带宽场景下很有优势。
  1. protobuf通过编译工具生成数据读写代码,提高开发者编码效率。
    支持向上游传递超时时间,让上游在发现超时时主动决定如何执行后续操作,http1.1则会直接断开连接。
  • 缺点
    1.只有少数浏览器支持gRPC。
    2.移动端网络可能在wifi及4G频繁切换,无法体现出长连接以及多路复用的优势。
    3.编码后数据可读性低,json则具有很高的可读性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值