微服务内部怎么"说话"?REST、GraphQL 与 gRPC(Protobuf)架构演进

微服务内部怎么通信?本文对比 REST(易过度/不足拉取)、GraphQL(按需取字段)与 gRPC(HTTP/2+Protobuf 强类型二进制),并给出对外用 REST/GraphQL、内部高频互调用 gRPC 的实在分层选型建议。

你公司把一团大单体拆成了十几个微服务,每个服务各管一摊:用户、订单、库存、支付……看起来清清爽爽。可你很快发现一个怪现象:前端想显示一个"我的订单"页面,后台竟然要发起五六次网络请求,东拼西凑才凑齐数据;有时候又反过来,明明只要用户名和头像,接口却把用户的整套资料、地址、订单历史全丢回来了。这种"内部通话"既慢又浪费带宽,还特别容易出错。微服务拆得越细,这个问题越严重。那么,微服务之间到底该怎么"优雅地说话"?REST、GraphQL、gRPC 这三套方案,分别给出了不同答案。

REST:人人都懂,但容易"说多"或"说少"

REST 是今天最普及的 API 风格,它其实不是某个具体协议,而是一种"架构风格":用 HTTP 的几个动词(GET/POST/PUT/DELETE)去操作"资源",每个资源对应一个网址,比如 /users/123、/orders/456。它的好处是举世皆懂——任何语言、任何浏览器、任何抓包工具都能直接上手,CDN 还能天然帮你做缓存。所以对外公开给第三方用的 API,REST 永远是安全牌。

但 REST 的软肋在于:服务器决定返回什么,客户端只能照单全收。这就引出两个经典毛病。一个是过度拉取(Over-fetching):你只想拿用户的昵称和头像,结果接口把年龄、地址、注册时间、订单列表全塞给你了——在慢吞吞的 3G 网络或手机上,这就是实打实的流量和电量浪费。另一个是拉取不足(Under-fetching):你要的数据分散在好几个资源里,于是得一个一个去请求,来来回回好几趟。有人实测,把 REST 换成 GraphQL 后,返回的数据量能减少 大约 93%。此外,REST 用的 JSON 是纯文本,字段名又长又啰嗦,双方还得靠约定来"假装"有类型,弱类型在大型团队协作里很容易踩坑。

GraphQL:客户端点菜,要啥给啥

GraphQL 是 Facebook 在 2012 年为了搞定慢速移动网络上的过度拉取问题而发明的。它彻底反了过来:不再由服务器定死每个接口返回什么,而是由客户端自己写查询、精确指定要哪些字段。整个 API 只有一个入口(通常叫 /graphql),你写一段结构化查询,比如"只要 user 的 name 和 avatar",服务器就只回这两个字段,一分不多。

这招漂亮地解决了过度拉取和拉取不足:手机端要"名字+缩略图",网页端要"完整详情+评论",它们对着同一个后端写不同的查询就行,一次请求就能把嵌套的关联数据全拿齐,不用东跑西跑。GraphQL 还让 API 演进变得轻松——加字段、标记旧字段废弃,客户端各取所需,不用搞一堆版本号。

不过天下没有免费的午餐。GraphQL 把复杂留给了后端:服务器得有一套"解析器(resolver)"和 schema,客户端也得学会构造合法查询,而不是简单敲个 URL。最著名的大坑是N+1 查询问题——你要 100 条帖子,每条帖子的"作者"字段各自触发一次数据库查询,结果变成 101 次查询而不是 2 次,能把数据库打挂。解法是用 DataLoader 这类"批量合并"工具。另外,REST 那种靠网址就能白嫖的 HTTP 缓存,在 GraphQL 这里基本失效,得额外做缓存策略;一个写得过深的查询还可能被坏人拿来做拒绝服务攻击,所以得加查询深度限制。

gRPC:微服务内部的"高速公路"

如果 REST 是"人人都能走的省道",GraphQL 是"按需点菜的自助餐",那 gRPC 就是微服务之间专属的高速公路。它是 Google 在 2015 年开源的高性能 RPC 框架,专治"内部服务高频互调"这种病。

gRPC 跟前面两位最大的不同有三点。第一,它跑在 HTTP/2 上,而不是老旧的 HTTP/1.1。HTTP/2 支持多路复用:多个请求能在同一条 TCP 连接上同时跑,互不排队,彻底解决了 HTTP/1.1 那个"队头阻塞"的老毛病——想象一条单车道上大卡车堵住后面所有车,HTTP/2 直接给它改成多车道。第二,它用 Protobuf(Protocol Buffers)二进制序列化,而不是 JSON 文本。数据被压成紧凑的字节流,用字段编号代替啰嗦的键名,体积小到只有同等 JSON 的 三成左右,序列化速度快 20 到 100 倍。第三,它强类型 + 代码生成:你先写一份 .proto 契约文件,工具自动生成 Go、Java、Python、C++ 等十多种语言的客户端和服务端代码,两边接口在编译期就被锁死,从根上杜绝"我对你传错字段"的尴尬。

更厉害的是 gRPC 原生支持四种通信模式:最常见的一元请求-响应(一个问一个答),以及服务端流式、客户端流式、双向流式。这意味着它不只是"问一句答一句",还能像聊天一样双向实时收发,特别适合实时推送、大文件分片、聊天室这类场景。性能上,同等数据量下 gRPC 比 REST 快大约 3 到 7 倍,平均延迟能从 REST 的 45 毫秒降到 12 毫秒左右,吞吐量能从每秒 2200 次请求冲到 8500 次。它还内置了超时、取消、拦截器、负载均衡、TLS 加密这些"企业级"能力。

当然 gRPC 也有代价:它的二进制报文人眼根本读不懂,没法用 curl 随便测;浏览器原生不支持,得加 grpc-web 代理层;契约一变就得重新生成代码,对快速迭代的业务有些束缚。所以它的主场很明确——只在你自家基础设施内部、你同时掌控客户端和服务端的地方

一个具体数字:同样一份数据,JSON 和 Protobuf 差多少

光说"二进制更小"有点虚,给个直观例子。假设要传一个用户对象,JSON 大概长这样:{"user_id":12345,"name":"张三","email":"zhangsan@example.com","is_vip":true}。你一眼就能看出,光那些键名就占了快一半篇幅,真正的"干货"被啰嗦的结构吃掉了。换成 Protobuf,数据被编成紧凑的字节流,字段不再写名字,而是用定义好的编号(比如 1 代表 user_id、2 代表 name)来标识,同样的信息体积能缩到原来的 三成左右。在网络里飞来飞去的数据少了七成,对微服务之间每秒成千上万次调用来说,省下的带宽和 CPU 解析时间非常可观。再叠加 HTTP/2 的多路复用,一条连接就能同时扛多个请求,不用每次都重新握手建连,延迟进一步往下压。

到底怎么选:按"边界"而不是按"流行"

说了这么多,关键不是"哪个最先进",而是"谁是调用方、要什么"。给你一张实在的取舍表:

  • 对外公开 API、给第三方或浏览器用:选 REST。大家都会、工具齐全、CDN 缓存白送,集成门槛最低。
  • 多个客户端(网页/iOS/安卓)对着同一份复杂数据、各自想要不同切片:选 GraphQL,但一开始就要上 DataLoader 防 N+1,并做好缓存和查询限制。
  • 内部微服务之间高频互调、两边都是你自己人:选 gRPC,性能和强类型契约省下的麻烦远超学习成本。

千万别因为"GraphQL 听起来很潮"就给一个本来三个 REST 接口就能搞定的简单系统硬套 GraphQL,那纯粹是给自己找运维负担。过度拉取往往只是"接口设计得跟真实用法不匹配"的表象,精心设计好用途明确的 REST 端点,常常用低得多的复杂度就解决了。

真实世界里,它们常常"三个一起上"

最成熟的架构往往是按边界分层混用,而不是二选一。比如:最外层对公众和浏览器暴露 REST 或 GraphQL,让外部开发者几分钟就能接上;而这些 API 背后站着一堆内部微服务,它们之间全用 gRPC 高速互调。对外讲究"好接",对内讲究"够快",各取所长。Netflix、Google 这类大厂的内部服务通信,gRPC 早就是事实标准。

给你的一句话总结

  • REST 简单通用,但服务端定死返回内容,容易过度拉取或拉取不足,JSON 又大又弱类型。
  • GraphQL 让客户端自己点菜、一次拿齐所需字段,治好了过度/不足拉取,但要小心 N+1 和缓存难题。
  • gRPC 基于 HTTP/2 + Protobuf 二进制 + 强类型代码生成,是内部微服务通信的性能王者,还支持流式。
  • 选型看"边界":对外 REST/GraphQL,内部高频 gRPC。
  • 真实生产环境常三者并存:对外友好、对内高速。

延伸阅读

更多相关攻略推荐:Ollama AI系列(2):VPS上的AI推理与API应用Ollama AI系列(2):VPS上的AI推理与API应用【CPU选型 01】搭载 AMD Ryzen 9950X 的 VPSARM / Ampere 席卷 VPS:性价比真香还是兼容陷阱?Ollama AI系列(2):VPS上的AI推理与API应用