随着应用程序开发的进展,除了各种事情外,我们对计算能力的关注不太担心。随着云提供商的出现,我们不太担心管理数据中心。一切都在几秒钟之内可用,而且需求也是如此。这导致
随着数据规模的增加,我们进行了诸如序列化,挑选和运输成本之类的活动。尽管我们并不担心计算资源,但延迟已成为开销。我们需要减少运输。过去已经开发了许多消息协议来解决这个问题。肥皂很大,而休息是一个修剪的版本,但是我们需要一个更有效的框架。远程过程调用(RPC)进来。
在这篇博客文章中,我们将了解RPC是什么以及RPC的各种实现,重点是GRPC,这是Google的RPC实现。我们还将与RPC进行比较,并了解GRPC的各个方面,包括安全性,工具等。所以,让我们开始!
什么是RPC?
RPC代表远程过程调用。该定义在名称本身中。过程调用只是平均函数/方法调用;这是遥不可及的遥远单词。如果我们可以远程调用函数呼叫怎么办?
简单地说,如果一个函数位于服务器上并且为了从客户端端调用,我们是否可以像方法/函数调用一样简单?从本质上讲,RPC的作用是给客户的幻觉是它正在调用本地方法,但实际上,它在远程计算机中调用了一种抽象网络层任务的方法。这样做的好处是,合同保持非常严格和透明(我们将在文章稍后讨论)。
RPC调用中涉及的步骤:
RPC序列流
这就是典型的休息过程的样子:
rpcs将过程归结为下方:
这是因为与提出请求相关的所有并发症现在都从我们这里抽象出来(我们将以代码生成来讨论这一点)。我们需要担心的只是数据和逻辑。
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表示数据的位置(如name
,id
和has_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);
}
在这里,HelloRequest
和HelloResponse
是消息,而HelloService
正在公开一个称为SayHello
的Unary RPC,它将HelloRequest
作为输入,并将HelloResponse
作为输出。
如前所述,HelloService
目前包含一个单一的RPC。但是它可以包含多个RPC。此外,它可以包含各种RPC(单/客户端流式/服务器端流/双向)。
为了定义流rpc,您要做的就是在请求/响应参数之前的前缀“ Streaming RPCs proto definitions, and generated code。
”。在上面的代码键链接中:
- streaming.proto:此文件是用户定义
- streaming.pb.go和streaming_grpc.pb.go:这些文件是在运行proto compiler command命令时自动生成的。
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身份验证和授权在两个级别上有效:
- 呼叫级身份验证/授权通常通过在呼叫时在元数据中应用的代币进行处理。 Token based authentication example。
- 渠道级的身份验证使用在连接级别应用的客户端证书。它还可以包括呼叫级身份验证/授权凭证,以自动应用于频道上的每个呼叫。 Certificate based authentication example。
这些机制都可以用来帮助获得服务。
中间
在休息中,我们将中间件用于各种目的,例如:
- 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-pkg
说protoc --proto_path=packaging packaging/*.proto --go_out=packaging
。这意味着protoc
(编译器)将packaging/*.proto
中的所有文件转换为其等效的GO文件,如flag --go_out=packaging
在packaging
目录本身中所示。
其次,在处理器中,已定义了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.proto
和processor.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。
这是您如何做的步骤:
-
在左侧栏中打开Postman,goto apis,然后单击“+”符号以创建新的API。在“弹出窗口”中,输入“名称”,“版本”和“模式”详细信息,然后单击Create [除非您需要从GitHub,Bitbucket等某些来源导入]。
-
请记住,导入在这里不起作用,因此最好将所有依赖的原蛋白质放在一个地方。
-
上述步骤将有助于保留未来使用的合同。
-
然后单击“新的”,然后选择“ GRPC请求”,输入URI,然后从保存的API列表中选择原型,最后输入您的请求消息并点击调用
在上述步骤中,我们发现了通过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。
进一步读取