为了账号安全,请及时绑定邮箱和手机立即绑定

gRPC:五年后,它还值得用吗?

难以置信,我加入Torq已经快五年了。仿佛就在昨天,创始团队还在一个临时办公室里聚会——那时离新冠疫情爆发只剩四周,临时办公室其实是一个电视剧拍摄场景。

在那些早期的日子里,我们正在做出关于将构成Torq基础的技术堆栈的重要决定。其中一个记忆犹新的决定是我们决定避免在未来的项目中使用OpenAPI/Swagger和Go。这个选择源于我们过去的共同经验。

我们中的一些人之前在 Luminate Security 工作过,在使用 OpenAPI(即 Swagger)与 Go 时,我们遇到了一些困难。在 Luminate,我们的所有微服务和前端通信都使用 REST,并使用 Swagger 文件定义。问题在于当时用 Go 从 Swagger 生成客户端和服务的工具不成熟。这导致每个开发人员都不得不以各自的方式创建客户端。我们不想再遇到同样的问题。因此,我们决定在新项目中彻底采用 gRPC 和 Protobuf。

我们尝试了几种不同的方法来设置我们的proto文件,并参考了这篇介绍。并开始思考是否需要这样的API网关,参考了这篇指南。经过一段时间,我们最终确定了这种设置。

回过头来看,选择了gRPC作为我们唯一的通信协议是一个非常明智的选择。这给我们带来了不少好处:

  1. 这帮助我们保持了向后兼容性,随着我们的系统进化。
  2. 我们可以通过代码检查工具来强制执行标准,确保代码质量的一致。
  3. 客户端与服务器都使用相同的生成代码,减少了差异性。

这些因素使我们的工程师在不同的微服务之间工作时更加轻松,避免了意外问题。此外,我们的共享代码库带来了许多内置的好处,包括标准化的身份验证、授权和可观察性中间件。这种方法极大地简化了我们的开发流程,并且显著提高了系统的整体可靠性。

即使现在,如果我要从零开始一个新项目,我一定会选择 gRPC 作为我的主要通信协议。在过去几年中,随着 buf.build 团队的卓越贡献,gRPC 生态系统已经显著成长和改进。他们的工作大大提升了 gRPC 工具和使用体验,使其成为现代软件开发项目的更佳选择。

该项目在多个方面进行了改进,但最重要的改进是以下这些。

https://buf.build

生成代码

五年前,当我们开始这段旅程时,protoc编译器工具是唯一可用的用于处理gRPC和Protocol Buffers数据的工具。这个工具虽然功能强大,但上手难度大,使用体验类似传统的Linux命令行工具——不够友好。

为了改善这种状况,我花了几个星期创建了一个Docker容器封装器。容器封装了protoc,以及我们内部使用的所有的代码生成插件,并包含了一些将所有这些内容粘合在一起的bash脚本。我们的主要目标是提升我们团队的开发者体验。我们希望消除每位工程师单独安装protoc及其插件的需要,并使生成Go或TypeScript资产成为开发者可以执行的单一命令。

这种方法确保团队中每个人在开发过程中使用相同的工具版本。它大大减少了设置时间并消除了版本冲突的潜在问题,使我们的工程师可以将更多精力投入到实际开发中,而不是工具配置。

今天,多亏了 buf generate 命令,事情变得简单多了。要让一个项目运行起来,你现在只需要在项目目录中创建两个文件,就可以开始了。

  • buf.yaml — 配置文件,定义了您的 proto 目录布局并规定了您希望施加的 lint 规则
  • buf.gen.yaml — 由 buf generate 命令使用的配置文件,生成集成的代码,用于您选择的语言

这种简单的设置达到了我们之前使用Docker镜像方法相同的效果。它允许开发者编译proto文件及其所有依赖项,使用各种不同的代码生成器,或集成自定义插件或工具。最好的部分是开发人员可以在不安装或配置本地机器上的任何内容的情况下完成所有这些操作。这种简化的流程极大地提高了效率,同时也减少了设置中的潜在问题。

模块依赖

典型的模块依赖情况,你最好还是不要这样做。

在我们刚开始使用gRPC/protos时,我们面临的一个重要挑战是,是否重用其他微服务中的proto消息,或者每个服务都重新定义。虽然通常来说不重用是最好的,但在基础设施消息方面有一些例外情况。

当时,没有最佳实践可以跨多个仓库共享proto文件。虽然我更倾向于通过使用多个仓库来进行代码组织,但单仓库代码库(这似乎是谷歌的首选方法)在这方面具有优势,因为你可以简单地引用其他服务的proto文件,这些文件位于另一个目录中。为了在多仓库设置中实现代码共享,我们依赖于一个名为protodep的工具。这个简单的proto文件依赖管理器本质上是从其他微服务的GitHub仓库中复制文件,并将它们放在与你自己的proto文件相邻的目录中(类似于几年前使用的go vendor)。我在[之前的博客文章]中对protodep进行了详细说明。

幸運的是,這些問題現在已經成為過去式了,這都要感謝Buf Schema Registry (BSR)。BSR作為管理和演進Protobuf API的中心樞紐,提供了幾個關鍵的好處,比如:

  1. 它有助于保持不同版本API之间的兼容性。
  2. 它简化了依赖管理。
  3. 它通过自动生成各种编程语言的客户端代码,确保客户端能够可靠且高效地使用API。
  4. 它会自动为您的gRPC服务和proto消息生成交互式文档,方便查看和使用。

可以将BSR视为您proto文件的全方位构建系统。其中一个最重要的功能是能够简单地管理proto模块之间的依赖关系。单凭这一功能,就解决了我们在多仓库环境中共享和管理proto文件版本时遇到的许多问题。

    # buf.yaml 示例,包含外部模块依赖  
    version: v2  
    # ....  
    # 工作区中所有模块共享的依赖项。这些依赖项必须托管在 Buf 架构注册表中。  
    # 这些依赖项的解析结果将会存入 buf.lock 文件中。  
    deps:  
      - buf.build/acme/paymentapis # 最新的提交。  
      - buf.build/acme/pkg:47b927cbb41c4fdea1292bafadb8976f # 该提交 '47b927cbb41c4fdea1292bafadb8976f'。  
      - buf.build/googleapis/googleapis:v1beta1.1.0 # 该标签 'v1beta1.1.0'。
在前端使用gRPC技术

当我告诉人们我们在前端代码中使用 gRPC-web 时,他们常常显得很惊讶的样子。我没有遇到过其他公司在使用它,但既然有工具支持,估计还是有一些公司在用。

使用一种 gRPC-web 带来了些问题(或者说是挑战)。

首先,它采用了一种二进制格式,你无法轻易地用浏览器的网络检查器进行查看。所有内容都以base64编码,这增加了前端调试工作的难度,并可能会让你的前端工程师感到沮丧,因为他们不能使用他们熟悉的工具,从而减缓他们的开发进程。有一个名为gRPC-Web 开发者工具的Chrome插件可以帮助检查网络流量,但在我看来,它不如Chrome内置的检查器那么好。

当你使用 gRPC-web 时,需要在后端服务器上设置一个代理服务器,将 gRPC-web 格式转换为 gRPC 格式,以便您的服务能够使用。你可以用 Envoy 来做这件事,但并不是每个人都在用。如果你使用 nginx,则需要在服务中自己添加转换。这样做可能会引发一些安全问题,因为可能不了解它支持的所有协议类型,你的安全检查可能不会按预期的那样工作。例如,gRPC-Web 支持WebSocket 用于客户端流,但你配置的 HTTP 处理程序中间件可能不会在那里被调用。

因为 gRPC-web 采用的是 POST 方法,所以你的调用将无法启用缓存。

另一个问题是(也可能是件好事),当安全团队试图测试你的应用程序时,他们可能没有通常用于测试gRPC-web的工具。这可能会被认为是一种通过 obscurity 实现的安全性。

幸好現在有一個叫做connectrpc的替代方案,它是由buf.build團隊提供的。它作為一套protoc插件,可以生成高質量的TypeScript客戶端,雖然本來支持grpc-web傳輸協議,還支持它自己的基於JSON的協議,這個協議還支持緩存。

connectrpc 的实际应用 (来源:https://connectrpc.com/

服务网状架构

你可能知道也可能不知道,Kubernetes 的默认负载均衡在 gRPC 中可能效果不佳。原文链接:Kubernetes 的默认负载均衡在 gRPC 中可能效果不佳

这是因为gRPC是基于HTTP/2构建的,而HTTP/2设计为使用单一的长连接,在此连接上可以同时传输多个请求——这意味着在同一连接上可以同时存在多个请求。通常来说,这很棒,因为它大大减少了连接管理的开销。然而,这也意味着(正如你可能想象的那样)连接级别的负载均衡就不再那么有用。一旦连接建立起来,就不再需要进行负载均衡了。所有的请求都会固定到同一个目标Pod上,如图所示(https://kubernetes.io/blog/2018/11/07/grpc-load-balancing-on-kubernetes-without-tears/):

为了克服这个问题,并得益于其出色的可观测性和安全性,我们自一开始就是linkerd的忠实用户。

除了为gRPC提供负载均衡之外,它还具有多个优势。

  1. 内置了针对所有网状服务的“高级”指标,例如请求量、成功率和延迟分布。
  2. 无需配置的mTLS,确保Pod之间传输的数据是加密的
  3. 流量授权策略——你可以限制特定服务(或服务上的HTTP路由)的通信,只允许来自某些特定服务的请求
我还会用gRPC吗?

完全正确,我首选的协议还是gRPC,这个如下栈:

  1. buf.build 用于编译生成适用于各种编程语言的 proto 文件,或者如果你使用 BSR,你甚至不必自己编译它们,只需使用 BSR 的代码生成功能即可。
  2. BSR 用于模块依赖管理,代码审查,向后兼容性验证和 API 文档管理
  3. 服务间通信协议:gRPC over linkerd
  4. 前端到后端 — connectrpc 使用 JSON 格式的数据
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消