在WePay上将API从REST迁移到gRPC
2020-12-18 21:34
标签:局限 客户 java cloud 复杂 包装 没有 guide res 在服务网格系列的前几篇文章中,我们讨论了如何设置服务网格基础结构以使微服务和负载平衡架构现代化,以及如何确保服务网格基础结构高度可用,以便我们可以使用其所有功能。随时随地都有出色的功能。 在本文中,我们将把注意力转移到我们的微服务如何使用服务网格相互通信上。具体来说,什么有效负载用于在它们之间发送的请求,以及我们如何从一个迁移到另一个。我们将比较当前基于代表性状态转移(REST)的有效负载和高性能的远程过程调用(gRPC)框架,并克服采用,使用和迁移到gRPC的挑战。 如我们之前的文章所述,我们目前在Google Kubernetes Engine(GKE)中运行许多微服务,其中一些微服务与同一数据中心中的其他微服务进行通信,形成微服务的组或图,以实现某些业务逻辑。整个: 图1:更大的服务图需要更快的通信和更轻松的API管理 由一组这些微服务组成的每个服务图可以在微服务之间进行传递通信,即微服务Service X可能正在向该组中的N个服务发送请求,而N个服务可能正在向M个服务发送请求,并且以此类推。这些微服务之间进行的请求(如图1所示)包括RESTful有效负载,这些有效负载使用“ HTTP / 1.1”发送。 随着基础架构中微服务数量的增加,为微服务提供通信能力的平台的可扩展性和维护性也随之提高。具体来说,当我们开始考虑将任务关键型微服务迁移到gRPC时,我们希望获得多语言和平台支持,可伸缩性,客户端和服务器之间的持久和可重用连接(使用‘HTTP / 2‘和Linkerd),以及bi转向gRPC进行双向流式传输。 图2:图中的一组服务以多种数据序列化格式进行通信 具有仅REST的API的旧版服务正在缓慢地采用和采用gRPC,以提高我们整体产品的性能。图2显示了一组服务,它们在REST和gRPC中相互通信,因为我们的服务正在迁移到gRPC。在文章的后面,我们将介绍如何更新服务以接受两种序列化格式,直到将所有API都迁移到gRPC。 随着我们在基础架构中添加了新的微服务和语言,gRPC产品对我们变得越来越重要,特别是在通过基础架构的可伸缩性使开发更容易并且使开发人员更容易进行微服务方面。gRPC文档详细介绍了在每种情况下使用gRPC的重要性,而我们最近召开的gRPC重点会议则更多地讨论了一些用例以及在类似基础架构中可以使用的更重要的功能。 在WePay的微服务世界中,每个微服务都使用带有JSON有效负载的REST作为彼此通信的标准方式。REST在服务到服务的通信中具有其优势: 随着基础架构中微服务数量的增加,服务图变得更加复杂,结果,使用REST进行通信存在痛点和局限性: 考虑到这些痛苦点,gRPC似乎是我们改善基础架构中微服务通信的不错选择。 在gRPC中,客户端应用程序可以直接在不同机器上的服务器应用程序上调用方法,就好像它是本地对象一样,从而使创建分布式服务变得更加容易。 图3:gRPC客户端将原始请求发送到gRPC服务并接收原始响应(源) gRPC有其自身的优点: Protobuf是文本文件,用于定义消息的结构并具有“ .proto”扩展名。它们紧凑,强类型并支持向后兼容。它们用于定义服务以及请求和响应数据的结构和类型。 代码1:具有单个RPC方法“ SayHello”的HelloWorld gRPC服务 作为有效载荷的Protobuf具有更好的序列化/反序列化为二进制格式。这使其比JSON更快。而且,客户端可以接收类型化的对象,而不是自由格式的JSON数据。 图4:JSON与协议缓冲区 每种语言都有其自己的protobuf编译器,可将这些文件转换为代码。例如,如果我们试图获取Java中生成的代码以用于代码1中的服务,则编译器将生成: 代码2:用Java自动生成的HelloWorld实现基类 代码3:Java客户端的HelloWorld存根 代码4:在Java中为protobuf消息生成代码 gRPC通信通过HTTP / 2进行。HTTP / 2通过使用标头字段压缩并允许在同一连接中进行并发交换,可以更有效地利用网络资源并减少延迟。在gRPC中,连接和交换称为通道和调用。通过这种方式,gRPC有助于减少为每个请求创建新连接的等待时间,这是将REST用于更复杂的服务图的限制之一。 基于上述好处,我们决定将REST服务迁移到gRPC。我们考虑了许多迁移策略。其中一种方法是构建gRPC应用程序,并使用grpc-gateway生成反向代理服务器,该服务器将RESTful JSON API转换为gRPC。这要求在protobufs中的gRPC定义中添加自定义选项,并添加一个运行此反向代理服务器的附加容器。 将服务迁移到gRPC的一些重要要求包括: 基于这些标准,我们决定将gRPC服务器作为使用REST框架的应用程序中的线程运行。 图5:WePay上的gRPC服务和客户端的结构 结果,我们构建了一个可以插入到我们的微服务中的共享库。该共享库包括一个监视服务,该服务实现RPC方法以检查gRPC服务的运行状况。共享库提供了一个“ GrpcServerBuilder”,其中包含: 开发人员使用共享库提供的GrpcServerBuilder,为微服务添加gRPC服务实现并构建gRPC服务器,这将确保监控我们所有微服务运行状况的标准模式。 正如我们在早期博客中提到的在WePay上使用Linkerd作为服务网格代理一样,Linkerd开箱即用地支持HTTP / 2和gRPC。但是将gRPC调用路由到适当的Kubernetes服务的设置很棘手。在服务网格中,通过在路径中提供服务名称,将来自客户端的REST调用重定向到Kubernetes中运行的服务。例如,如果我们有一个 gRPC使用HTTP / 2作为传输。gRPC服务与客户端之间的请求/响应遵循规范。请求的主要字段之一是“路径”。请求的路径设置为: Linkerd可以使用路径或标头作为标识符来将请求从客户端路由到服务。对于gRPC,选择标识符时存在服务器方面的挑战: 这些情况使得很难将路径用作标识符。我们通过强制客户端将自定义标头“服务”设置为Kubernetes服务名称来解决该问题。并且Linkerd配置为将标头“ service”用作gRPC调用的标识符。 图6:演示使用请求标头作为标识符的gRPC调用路由。 gRPC服务具有其自己的生命周期: 在确定协议缓冲区的生命周期时,需要考虑一些标准。 本节介绍如何满足以下条件并设置gRPC服务的生命周期。 图7:WePay上gRPC的生命周期。 由于我们在WePay使用面向服务的体系结构,因此我们需要为许多服务编写和维护protobuf文件。我们决定维护一个中央git存储库,以存储包含WePay上所有gRPC服务器定义的protobuf文件。 然后,使用我们的持续集成(CI)服务器中的prototool验证这些原型文件,然后在git存储库中的主版本中创建发布标签以进行更改。 要实现gRPC服务器/客户端,我们需要从protobufs生成代码。为此,我们使用release标记将protobuf提取到应用程序存储库中,并使用protoc编译器以所需的语言生成代码,并使用它们来实现服务器/客户端。 当前,我们使用RAML,Swagger等工具生成微服务的REST API文档。我们常见的微服务构建过程生成API文档。同样,我们为微服务建立了构建过程,该过程也配置为使用protoc -gen-doc插件生成gRPC文档。 gRPC具有许多优点,但是并非REST中的所有微服务都可以迁移到gRPC。例如,迁移外部服务可能需要使用REST API的客户端进行返工。 在将现有的REST微服务迁移到gRPC时,对我们来说gRPC面临一些开发挑战,而以下是最重要的挑战。 直到最近,gRPC还不支持浏览器。当前,让浏览器客户端访问gRPC服务的一种流行方法是使用grpc-web。浏览器客户端使用特殊的网关代理连接到gRPC服务。我们没有将grpc-web用于外部服务,因为当我们做出此决定时,GA版本不可用。我们的前端也有许多JSON模式,将所有这些转换为protos并非易事。此更改还需要更新我们所有的SDK。 我们的某些API需要识别请求/响应数据之间的区别: 使用proto3,我们无法轻松识别这些差异。虽然,我们可以使用Google包装器来识别是否设置了该字段。我们要标识空值的潜在用例是请求的更新类型,服务器在其中接收将某些字段设置为空的请求。即,如果后端中存在现有消息,并且我们需要将其中一个字段设置为null(在protobuf世界中也意味着清除该字段)。我们可以在这些类型的场景中使用场掩码。 当前,使用REST框架编写的微服务假定请求处理程序是HTTP堆栈的一部分,并具有执行中间件的能力,例如身份验证检查,请求/响应日志记录,度量跟踪等。 没有任何现有的gRPC框架与我们使用的HTTP框架具有同等的功能。但是,gRPC提供了“拦截器”来启用这些中间件。我们为gRPC服务器所需的每个功能构建拦截器,并将它们打包为所有这些微服务都将使用的通用库。 协议缓冲区语言具有两种语法版本:Proto2和Proto3。gRPC支持两个版本。Proto2和Proto3相似,但差别不大。 表1:proto2和proto3之间的比较 很难决定要使用哪个版本的协议缓冲区。如上面比较表(表1)所述,从proto2到proto3有一些向后不兼容的更改。因此,最好不要更新是否已使用proto2构建gRPC服务。如果我们想使用诸如设置自定义默认值,识别缺失字段,必填消息字段之类的功能,那么我们可以使用proto2。如果我们想利用新功能,并为仅在proto2中可用的功能使用替代功能,则可以选择proto3。 总之,根据微服务的数量,性能和可维护性要求,我们决定使用gRPC构建新的微服务,并在适用的情况下将所有现有微服务迁移到gRPC。 这给我们: 现在,我们能够改善整个基础架构中的服务到服务的通信,我们需要保持这些服务正在运行的基础架构和平台为最新并不断改进。 在下一篇文章中,我们将研究如何管理基础结构的生命周期,以实现持续的基础结构改进,而停机时间为零,而又不影响生产服务的整体运行状况和性能。 在WePay上将API从REST迁移到gRPC 标签:局限 客户 java cloud 复杂 包装 没有 guide res 原文地址:https://www.cnblogs.com/a00ium/p/13943979.html从RESTful到gRPC
为什么要使用gRPC?
使用Protobufs的请求
连接重用
如何从REST迁移到gRPC?
使用服务网格进行通信
foo
运行REST服务的名为“ ”的Kubernetes服务,则使用Linkerd,可以使用路径“ foo/
”将客户端调用路由到该服务。
在为客户端构建的公共库中,我们添加了一个选项,将标头设置为目标Kubernetes服务名称。Linkerd使用此标头和标识符将请求从客户端路由到服务。CI / CD gRPC服务的生命周期
protos/
|-
迁移到gRPC的挑战
浏览器支持
Protobuf限制
gRPC和HTTP框架
Protobuf版本
特征
原型2
原型3
字段可以设置为NULL
没有
没有
必填消息字段
是
没有
能够为字段设置自定义默认值
是
删除默认值。设置为默认值的原始字段不会序列化
JSON编码
使用二进制protobuf编码
JSON编码的添加
UTF-8检查
不严格
严格执行
能够识别是否不包含缺少的字段,或者是否为其分配了默认值。
是
不可以。在Proto3中,当我们使用基本字段时,我们无法区分设置的默认值和未设置的字段。我们可以通过使用包装器(众所周知的原型)而不是基元来解决此问题。
结论