【译】gRPC:坏的部分

本文译文深入剖析了gRPC这款高性能RPC框架的不足,详细讨论了其陡峭的学习曲线、过度复杂的接口设计、不完善的HTTP/2兼容性、延迟的HTTP/3支持、JSON映射局限及大消息处理难题,帮助开发者全面认识并探讨gRPC优化改进的可能方向。

 

原文链接🔗: https://kmcd.dev/posts/grpc-the-bad-parts/

gRPC 作为一款高性能的 RPC 框架,在 Google 内部取得了巨大成功,并显著改变了我们部署 API 的方式。然而,它并非完美无缺。创建一个支持代码生成和多语言的 RPC 框架,必然会面临诸多挑战。笔者使用 gRPC 已近十年,在此有必要反思其有待改进之处。

学习曲线:术语与复杂性

首先,我们从一些细节入手。“一元请求”(Unary Request)是指客户端向服务器发送单个请求并接收单个响应的调用。为何 gRPC 必须使用如此晦涩的术语?每次使用都需解释,实在令人困扰。

说到一元请求,gRPC 的实现也显得过于复杂。尽管 gRPC 的流式接口功能强大,但对于简单的 RPC 调用,它引入了不必要的复杂性。这增加了调试 gRPC 调用的难度,因为即使是一元请求也需要分帧,这本是流式传输的特性。Protobuf 编码已足够复杂,无需在非必要之处增加额外的 gRPC 分帧。此外,对于 Web API 而言,使用 cURL 示例来解释 gRPC 的使用方式非常困难。“需要启用服务器反射吗?”这句话我已说过无数次。

这种复杂性也体现在工具层面,尤其是强制的代码生成步骤。对于重视运行时灵活性的动态语言而言,这可能是一个障碍。此外,一些开发者可能会对需要额外构建步骤的技术望而却步。现代 Web 开发已包含众多构建步骤,再增加一个往往令人犹豫。

Web 兼容性:HTTP/2 与 HTTP/3

对 HTTP/2 的依赖最初限制了 gRPC 的普及,因为并非所有平台和浏览器都完全支持它。尽管情况有所改善,但在某些环境下仍是挑战。即使支持 HTTP/2,浏览器也缺乏处理 HTTP Trailers 的能力,因此目前浏览器仍无法“原生”使用 gRPC。gRPC-Web 通过避免使用 Trailers 解决了这个问题,但通常需要额外的代理支持,这非常麻烦。

延迟采用 HTTP/3 协议可能阻碍了 gRPC 充分利用其性能和效率优势。笔者深受 HTTP/2 的“队头阻塞”(Head-of-line blocking)问题困扰,而 HTTP/3 本可彻底解决这一问题。一个大力推广 HTTP/2 的框架,却在 HTTP/3 支持上如此挣扎,着实令人费解。

JSON 映射与 Prototext:标准化缺失

早期缺乏标准化的 JSON 映射是另一个失误。这使得习惯于 JSON API 的开发者难以接受 gRPC,我认为 gRPC 从未摆脱这种负面印象。Protobuf 类型与 JSON 之间的映射简化了与现有工具和系统的集成与互操作。当你说“是的,这是一种高效的二进制格式……但如果你想调试,可以设置这个标志来返回 JSON”时,Web 开发者会非常高兴。总之,现在 Protobuf 已有标准的 JSON 映射规则(以及反向),而 Protobuf 文本格式(Prototext)则显得多余。既然有了 JSON,文本格式便不再必要。不如将其废弃。如果大家同意,我愿意假装它从未存在过。

消息大小限制:分块的必要性

大多数 Protobuf 编码器/解码器都期望完全解析整个消息,然后将完整响应提供给消费者,但内存有限,有时我们需要处理更大的消息。有时,我们希望将这些大消息分段传输到其他地方,而不是将其全部保存在内存中。

因此,如果需要上传大型文件,我们需要实现某种文件分块机制。尽管分块是处理大文件的合理方案,但 gRPC 缺乏标准化的方法,可能导致实现不一致,增加开发工作量。

例如,以下是使用 gRPC 上传文件的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
syntax = "proto3";

package file_service;

service FileService {
  rpc Upload(stream UploadRequest) returns(UploadResponse);
}

message UploadRequest {
  string file_name = 1;
  bytes chunk = 2;
}

message UploadResponse {
  string etag = 1;
}

这既是 Protobuf 的优势,也是劣势。在 Protobuf 中定义这个概念非常容易,但实际实现却可能繁琐且容易出错。尽管 gRPC 的创建者 Google 已为其 API 找到了解决方案,但缺乏标准化的方法,让其他人需要重复造轮子。

你可能会想:“Google 在其大多数 API 中使用 gRPC,显然他们已经解决了这个问题。” 你是对的。他们确实有一个 gRPC 和 HTTP 版本用于下载(可能很大的)文件。我们可以直接比较 gRPCHTTP版本,gRPC 版本明显更复杂。

社区活跃度:依赖管理之殇

我发现 gRPC/Protobuf 社区的活跃度不高。一些网站缺乏明显的活动,可能会让人觉得 gRPC 停滞不前或维护不力。这可能会阻碍潜在的采用者,并减缓社区的增长。这可能是因为选择太多,导致人们难以在 GitHub 问题之外找到愿意讨论 gRPC 的人。

很长一段时间里,每当看到一个代码库使用 Protobuf,我都会发现一个奇怪的脚本,用于下载随机的 Protobuf 文件,以高度自定义的方式放置它们,并执行一系列极其复杂的 protoc 调用。只有 Google 才会认为不解决依赖管理问题就是解决依赖管理问题。Google 有自己的依赖管理方式,我们只能望其项背。

持续改进:未来可期

尽管我对 gRPC 提出了批评,但我希望这些评论是建设性的。读到这里的人应该知道,许多问题已经或正在得到解决:

  • 一些 gRPC 实现已支持 HTTP/3。ConnectRPC 让使用 HTTP/3 的 gRPC 变得非常容易。
  • 由于 Protobuf 规范已包含标准的JSON 映射,我不再需要担心文本格式。
  • 如果你知道去哪里寻找,gRPC 社区其实非常活跃。例如,Buf Slack 是一个很好的资源。
  • Buf CLI 是一款出色的 gRPC 工具。它完全取代了 protoc,并增加了 linting、破坏性变更检测、gRPC 版 cURL、与 Buf Schema Registry 的集成(真正的依赖管理!)等功能。此外,越来越多的工具开始支持 gRPC,例如 Postman、Insomnia 和 k6。

结论

尽管 gRPC 取得了不可否认的成功,但承认其不足之处至关重要,这有助于其持续发展和改进。通过解决学习曲线、兼容性、标准化和社区参与等方面的问题,我们可以释放 gRPC 的全部潜力,使其成为所有开发者都能轻松使用的工具

延伸阅读

再谈 gRPC 的 Trailers 设计


  • 本文长期链接
  • 如果您觉得我的博客对你有帮助,请通过 RSS订阅我。
  • 或者在X上关注我。
  • 如果您有Medium账号,能给我个关注嘛?我的文章第一时间都会发布在Medium。
true
最后更新于 Jul 17, 2024 09:35 CST
使用 Hugo 构建
主题 StackJimmy 设计