了解GRPC概念,用例和最佳实践
#go #grpc #framework #communication

随着应用程序开发的进展,除了各种事情外,我们对计算能力的关注不太担心。随着云提供商的出现,我们不太担心管理数据中心。一切都在几秒钟之内可用,而且需求也是如此。这导致
随着数据规模的增加,我们进行了诸如序列化,挑选和运输成本之类的活动。尽管我们并不担心计算资源,但延迟已成为开销。我们需要减少运输。过去已经开发了许多消息协议来解决这个问题。肥皂很大,而休息是一个修剪的版本,但是我们需要一个更有效的框架。远程过程调用(RPC)进来。

在这篇博客文章中,我们将了解RPC是什么以及RPC的各种实现,重点是GRPC,这是Google的RPC实现。我们还将与RPC进行比较,并了解GRPC的各个方面,包括安全性,工具等。所以,让我们开始!

什么是RPC?

RPC代表远程过程调用。该定义在名称本身中。过程调用只是平均函数/方法调用;这是遥不可及的遥远单词。如果我们可以远程调用函数呼叫怎么办?

简单地说,如果一个函数位于服务器上并且为了从客户端端调用,我们是否可以像方法/函数调用一样简单?从本质上讲,RPC的作用是给客户的幻觉是它正在调用本地方法,但实际上,它在远程计算机中调用了一种抽象网络层任务的方法。这样做的好处是,合同保持非常严格和透明(我们将在文章稍后讨论)。

RPC调用中涉及的步骤:

RPC Sequence Flow

RPC序列流

这就是典型的休息过程的样子:

Rest Flow

rpcs将过程归结为下方:

GRPC Flow

这是因为与提出请求相关的所有并发症现在都从我们这里抽象出来(我们将以代码生成来讨论这一点)。我们需要担心的只是数据和逻辑。

GRPC-什么,为什么和如何

到目前为止,我们讨论了RPC,这实质上意味着远程调用功能/方法调用。从而给我们带来了诸如strict contract definition的好处,即将数据的传输和转换降低等等。等等等。我们将在继续这篇文章时进行讨论。我们真正想深入研究的是RPC的实现之一。 RPC是一个概念,GRPC是基于它的框架。

RPC有各种实现。他们是:

  • grpc(Google)
  • 节俭(Facebook)
  • Finagle(Twitter)

Google的RPC版本称为GRPC,该版本于2015年引入,此后一直引起关注。它是微服务体系结构中最选择的通信机制之一。

grpc使用protocol buffers(这是开源消息格式)作为客户端和服务器之间通信的默认方法。此外,GRPC使用HTTP/ 2作为默认协议。

再次有四种类型的通信:

介绍了在GRPC-协议缓冲区又称Protobufs中广泛使用的消息格式。 Protobuf消息看起来如下:

message Person {
  string name = 1;
  string id = 2;
  string email = 3;
}

在这里,Person是我们要传输的消息(作为请求/响应的一部分),它具有fields name(字符串类型),id(字符串类型)和email(字符串类型)。数字1、2、3表示数据的位置(如nameidhas_ponycopter中),当它序列化为二进制格式时。

开发人员使用所有消息创建了协议缓冲区文件,我们可以使用协议缓冲器编译器(二进制文件)来编译书面协议缓冲区文件,该文件将生成所有实用程序类以及使用该消息所需的方法。例如,如上面的Person消息所示,具体取决于所选语言generated code will look like this

我们如何定义服务?

我们需要定义使用要发送/接收的上述消息的服务。

编写必要的请求和响应消息类型后,下一步是编写服务本身。
GRPC服务也在协议缓冲区中定义,它们使用服务和RPC关键字来定义服务。

查看以下原始文件的内容:

message HelloRequest {
  string name = 1;
  string description = 2;
  int32 id = 3;
}
message HelloResponse {
  string processedMessage = 1;
}
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

在这里,HelloRequestHelloResponse是消息,而HelloService正在公开一个称为SayHello的Unary RPC,它将HelloRequest作为输入,并将HelloResponse作为输出。

如前所述,HelloService目前包含一个单一的RPC。但是它可以包含多个RPC。此外,它可以包含各种RPC(单/客户端流式/服务器端流/双向)。

为了定义流rpc,您要做的就是在请求/响应参数之前的前缀“ Streaming RPCs proto definitions, and generated code

”。

在上面的代码键链接中:

grpc vs休息

我们确实谈论了GRPC。另外,还提到了休息。我们错过的是讨论差异。我的意思是,当我们有一个完善的,轻巧的交流框架以休息的形式时,为什么需要寻找另一个交流框架?让我们更多地了解GRPC关于休息以及每一个的利弊。

为了比较我们需要的是参数。因此,让我们将比较分解为以下参数:

  • 消息格式:协议缓冲区与JSON

    • 在所有数据大小(小/中/大)的协议缓冲区的情况下,序列化和避免速度更好。 Benchmark-Test-Results
    • 序列化JSON是人类可读性的,而Protobuf(以二进制格式)则不是。不确定这是否是不利的,因为有时您想在Google Developers工具或Kafka主题中查看请求详细信息,而对于Protobufs,您无法分辨出任何内容。
  • 通信协议:http 1.1 vs http/2

    • 休息基于HTTP 1.1; REST客户端和服务器之间的通信将需要建立的TCP连接,而TCP连接又有3向握手。当我们从客户端发送请求后从服务器获得响应时,此后不存在TCP连接。需要旋转新的TCP连接才能处理另一个请求。在每个请求上建立了TCP连接的建立,都累积了延迟。
    • 基于HTTP 2的So GRPC通过具有持久的连接遇到了这一挑战。我们必须记住,HTTP 2中的持续连接与劫持TCP连接并且数据传输未经监视的Web插座中的持续连接不同。在GRPC连接中,一旦建立了TCP连接,就可以重用多个请求。来自同一客户端和服务器对的所有请求均已多路复用到同一TCP连接。
  • 只是担心数据和逻辑:代码生成是一流的公民

    • 代码生成特征是GRPC通过其内置的原始编译器的原生。使用REST API,有必要使用Swagger等第三方工具以各种语言自动生成API调用代码。
    • 在GRPC的情况下,它抽象了编组/删除的过程,建立连接并发送/接收消息;我们都需要担心的是我们要发送或接收的数据和逻辑。
  • 传输速度

    • 由于二进制格式比JSON格式轻得多,因此GRPC的传输速度比REST的传输速度快7至10倍。
一起使用
功能 休息 grpc
通信协议 遵循请求响应模型。它可以与HTTP版本一起使用,但通常与HTTP 1.1 遵循客户端响应模型,并基于HTTP2。一些服务器具有解决方法可以使其与HTTP 1.1(通过REST网关)一起使用
浏览器支持 到处工作 有限的支持。需要使用gRPC-Web,这是网络的扩展名,基于HTTP 1.1
有效载荷数据结构 主要使用基于JSON和XML的有效载荷传输数据 默认情况下使用协议缓冲器来发送有效载荷
代码生成 需要使用Swagger等第三方工具生成客户端代码 GRPC对各种languages的代码生成有本机支持
请求缓存 易于在客户端和服务器侧上缓存请求。大多数客户/服务器本地支持它(例如通过cookie) 默认情况下不支持请求/响应缓存

暂时是GRPC没有浏览器支持,因为大多数UI框架仍然有限或对GRPC的支持有限。尽管在大多数情况下,GRPC是内部微服务通信的自动选择,但对于需要UI集成的外部通信并不相同。

现在,我们已经对两个框架进行比较:GRPC和REST。使用哪个以及何时?

  • 在具有多重轻量微服务的微服务体系结构中,数据传输的效率至关重要,GRPC将是理想的选择。
  • 如果具有多种语言支持的代码生成是必需的,则GRPC应该是首选框架。
  • 具有GRPC的流媒体功能,诸如交易或OTT之类的实时应用程序将受益于此,而不是使用REST进行轮询。
  • 如果带宽是一个约束,则GRPC将提供较低的延迟和吞吐量。
  • 如果需要更快的开发和高速迭代,则休息应该是首选。

GRPC概念

负载均衡

即使持续连接解决了延迟问题,它仍以负载平衡的形式提出了另一个挑战。由于GRPC(或HTTP2)也会创建持久的连接,即使存在负载平衡器,客户端也会与负载平衡器背后的服务器形成持久连接。这类似于粘性会话。

我们可以通过演示和代码和部署文件理解挑战,也存在in this repository

从上面的演示代码库中,我们可以发现负载平衡的责任落在客户端。这导致了这样一个事实,即GRPC的优势,即与此更改不存在持久连接。但是GRPC仍然可以用于其其他好处。

阅读有关load balancing in gRPC的更多信息。

在上述演示代码碱中,仅使用/展示了圆形式负载平衡策略。但是GRPC确实支持另一个基于客户端的负载平衡策略OOB称为“选择”。

此外,还支持custom client-side负载平衡。

清洁合同

在休息中,记录了客户端和服务器之间的合同,但并不严格。如果我们返回肥皂,则通过WSDL文件暴露合同。在休息中,我们通过Swagger和其他规定公开合同。但是缺乏严格性,我们不能肯定知道在开发客户端代码时服务器方面是否已更改合同。

使用GRPC,合同可以直接通过proto文件与客户端和服务器共享,或者是从proto文件中生成的存根。这就像在函数调用一样,但远程呼叫。而且,由于我们正在调用函数调用,因此我们完全知道我们需要发送什么以及我们期望作为回应。与客户建立联系,照顾安全性,序列化排序等的复杂性被抽象了。我们只关心数据。

让我们考虑Greet App的代码库。

客户端使用stub(从proto文件生成的代码)来创建客户端对象并调用远程函数调用:

import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
cc, err := grpc.Dial("<server-address>", opts)
if err != nil {
    log.Fatalf("could not connect: %v", err)
}
c := greetpb.NewGreetServiceClient(cc)
res, err := c.Greet(context.Background(), req)
if err != nil {
    log.Fatalf("error while calling greet rpc : %v", err)
}

同样,服务器也使用相同的stub(从proto文件生成的代码)接收请求对象并创建响应对象:

import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
func (*server) Greet(_ context.Context, req *greetpb.GreetingRequest) (*greetpb.GreetingResponse, error) {

  // do something with 'req'

   return &greetpb.GreetingResponse{
    Result: result,
      }, nil
}

他们俩都使用了从原始文件greet.proto生成的同一存根。

使用原始编译器生成了存根,生成的命令是this

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative internal/pkg/proto/*.proto

安全

GRPC身份验证和授权在两个级别上有效:

这些机制都可以用来帮助获得服务。

中间

在休息中,我们将中间件用于各种目的,例如:

  • Rate limiting
  • pre/post请求/响应验证
  • 解决安全威胁

我们也可以使用GRPC实现相同的目标。在GRPC中,词汇是不同的,它们被称为拦截器,但它们进行了类似的活动。

greet_app代码库的the middlewares branch中,我们集成了记录仪和普罗米修斯拦截器。

查看拦截器如何配置为在middleware.go中使用Prometheus和Logging软件包。

    // add middleware
    AddLogging(&zap.Logger{}, &uInterceptors, &sInterceptors)
    AddPrometheus(&uInterceptors, &sInterceptors)

,但我们可以将其他软件包集成到拦截器,以防止恐慌和恢复(处理异常),跟踪,甚至身份验证等。

Supported middlewares by gRPC framework.

原始文件的包装,版本控制和代码实践

打包

让我们关注the packaging branch

首先从Taskfile.yaml开始,任务gen-pkgprotoc --proto_path=packaging packaging/*.proto --go_out=packaging。这意味着protoc(编译器)将packaging/*.proto中的所有文件转换为其等效的GO文件,如flag --go_out=packagingpackaging目录本身中所示。

其次,在处理器中,已定义了2条消息,即cpu和gpu。尽管CPU是一个简单的消息,其中包含3个内置数据类型的字段,但另一方面,GPU消息具有另一种称为“内存”的自定义数据类型,以及与CPU消息相同的内置数据类型。 ``内存是一个单独的消息,并在另一个文件中定义。
那么,如何在processor.proto文件中使用内存消息?通过使用import

syntax = "proto3";
package laptop_pkg;
option go_package = "/pb";
import "memory.proto";
message CPU {
  string brand = 1;
  string name = 2;
  uint32 cores = 3;
}
message GPU {
  string brand = 1;
  string name = 2;
  uint32 cores = 3;
  Memory memory = 4;
}
syntax = "proto3";
package laptop_pkg;
option go_package = "/pb";
message Memory {
  enum Unit {
    UNKNOWN = 0;
    BIT = 1;
    BYTE = 2;
    KILOBYTE = 3;
    MEGABYTE = 4;
    GIGABYTE = 5;
  }
  uint64 value = 1;
  Unit unit = 2;
}

即使您尝试通过在提及导入后运行任务gen-pkg来生成触发文件,也会丢弃错误。默认情况下,protoc假设文件memory.protoprocessor.proto都处于不同的软件包中。因此,您需要在两个文件中提及相同的软件包名称。
可选的go_package表示编译器为GO文件创建一个软件包名称为pb。如果要创建任何其他Leganagun-d proto文件,则软件包名称为laptop_pkg

版本控制

GRPC破坏和非破坏变化可能有两种变化:

  • 非破坏的更改包括添加新服务,向服务添加新方法,将字段添加到请求或响应原始原型,并为枚举添加一个值
  • 破坏更改,例如重命名字段,更改字段数据类型,字段号,重命名或删除包,服务或方法需要服务版本
  • 为了区分原始文件的同名消息或服务,可以实现optional packaging

代码实践

  • 请求消息必须带有请求CreateUserRequest的后缀。
  • 响应消息必须带有请求CreateUserResponse的后缀。
  • 如果响应消息为空,则可以使用一个空对象CreateUserResponse或使用google.protobuf.Empty
  • 软件包名称必须有意义,并且必须版本化,例如:软件包com.ic.internal_api.service1.v1

工具

GRPC生态系统支持一系列工具,以使生活在非开发任务,诸如文档,GRPC服务器的REST网关,集成自定义验证器,绒毛等等非开发任务中更轻松。这里有一些工具可以帮助我们实现同样的工作:<<<<<<<< /p>

  • protoc-gen-grpc-gateway插件用于创建GRPC REST API网关。它允许GRPC端点作为REST API端点,并执行从JSON到Proto的翻译。基本上,您可以使用一些自定义注释定义GRPC服务,并且可以使用JSON请求通过REST访问这些GRPC方法。
  • protoc-gen-swagger grpc-gateway的配套插件。它能够根据GRPC网关所需的自定义注释生成Swagger.json。然后,您可以将该文件导入您选择的REST客户端(例如Postman),并对您暴露的方法执行REST API调用。
  • protoc-gen-grpc-web插件,允许我们的前端使用GRPC调用与后端通信。将来会有一篇关于此内容的单独博客文章。
  • protoc-gen-go-validators插件,允许为原始消息字段定义验证规则。它生成了用于原始消息的Validate() error方法,您可以致电如果消息与您的预定义的期望匹配,则可以验证。
  • protolint-一个插件将棉绒规则添加到原始文件中。

使用Postman进行测试

与使用Postman或任何等效工具(如失眠)测试REST API不同,测试GRPC服务并不舒服。

注意: GRPC服务也可以使用evans-cli等工具从CLI进行测试。但是对于这种反射需求(如果未启用了原始文件的路径为required),则在GRPC服务器中启用。该compare link显示了启用反射的方法以及如何进入Evans-CLI REPP模式。邮政输入Evans-CLI,可以从CLI本身测试GRPC服务,并且该过程在evans-cli GitHub page中进行了描述。

Postman具有测试GRPC服务的beta version

这是您如何做的步骤:

  1. 在左侧栏中打开Postman,goto apis,然后单击“+”符号以创建新的API。在“弹出窗口”中,输入“名称”,“版本”和“模式”详细信息,然后单击Create [除非您需要从GitHub,Bitbucket等某些来源导入]。
    Create new API

  2. 创建API后,然后转到定义并输入您的原始合同。
    Enter proto contract

  3. 请记住,导入在这里不起作用,因此最好将所有依赖的原蛋白质放在一个地方。

  4. 上述步骤将有助于保留未来使用的合同。

  5. 然后单击“新的”,然后选择“ GRPC请求”,输入URI,然后从保存的API列表中选择原型,最后输入您的请求消息并点击调用
    Create gRPC request

在上述步骤中,我们发现了通过Postman测试GRPC API的过程。测试GRPC端点的过程与使用Postman的REST端点的过程不同。要记住的一件事是,在创建和保存原始合同中,如5所示时,所有原始消息和服务定义都必须位于同一位置。由于没有规定可以访问Postman中的版本的原始消息。

结论

在这篇文章中,我们制定了有关RPC的想法,与休息和讨论差异进行了与之相似,然后我们继续讨论了RPC的实现,即Google开发的GRPC。

GRPC作为框架可能至关重要,特别是对于基于微服务的内部通信架构。它也可以用于外部通信,但需要休息网关。 GRPC是流媒体和实时应用的必不可少的。

GO的方式证明自己是服务器端脚本语言,GRPC正在证明自己是事实交流框架。

就是这样!随时与Hitesh/Pranoy联系以获取有关此主题的任何反馈和想法。

寻求帮助以建立DevOps策略或想将DevOps外包给专家?了解为什么这么多的创业公司和企业将我们视为best DevOps consulting & services companies

进一步读取