原文链接🔗: 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 上传文件的示例:
|
|
这既是 Protobuf 的优势,也是劣势。在 Protobuf 中定义这个概念非常容易,但实际实现却可能繁琐且容易出错。尽管 gRPC 的创建者 Google 已为其 API 找到了解决方案,但缺乏标准化的方法,让其他人需要重复造轮子。
你可能会想:“Google 在其大多数 API 中使用 gRPC,显然他们已经解决了这个问题。” 你是对的。他们确实有一个 gRPC 和 HTTP 版本用于下载(可能很大的)文件。我们可以直接比较 gRPC和HTTP版本,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 的全部潜力,使其成为所有开发者都能轻松使用的工具