一、接口协议基础:四个常用的协议,你真正用过几个?

做接口迁移,首先得把协议本身吃透。我见过不少团队,一上来就选 gRPC,结果业务场景根本不需要那么高的实时性。也有团队死磕 RESTful,接口越写越重,最后自己都维护不过来。

说白了,协议选型没有银弹。每种协议都有自己的脾气。今天我就从实战角度,把 HTTP、gRPC、WebSocket、GraphQL 这四个常用协议掰开揉碎聊聊。

1.1 HTTP 协议:最熟悉的陌生人

HTTP 你肯定天天用。但它真的那么简单吗?我跟你说几个坑。

  • 请求-响应模型:客户端发请求,服务端返回响应。一对一,串行处理。
  • 无状态:每次请求都是独立的。服务端记不住你是谁。、Session 都得自己维护。
  • 文本传输:Header 和 Body 都是文本。Payload 大了,格式解析开销不小。
  • 版本演进:HTTP/1.1 用文本协议,队头阻塞严重。HTTP/2 引入了二进制分帧、多路复用。HTTP/3 直接用 QUIC,丢包也不影响其他流。
我的经验: 我做过一个老系统迁移,HTTP/1.1 下 100 个请求排队,等得心焦。后来切到 HTTP/2,多路复用一开,性能直接翻倍。但 HTTP/2 的二进制帧解析在弱网环境下不稳定,得配合重试策略。嗯,这里要注意。

1.2 gRPC 协议:高性能的双向之道

gRPC 是 Google 开的。底层用 HTTP/2,数据用 Protobuf 序列化。我最早接触 gRPC 是在做微服务网关的时候,说实话,刚开始不适应,用久了就离不开。

  • IDL 先行:用 .proto 文件定义接口。服务端和客户端共享同一份契约。类型、字段、服务名都在 proto 里定死。
  • 四种通信模式:一元 RPC(简单请求-响应)、服务端流式、客户端流式、双向流式。你想想看,一个连接可以同时发多条消息,这在实时场景下有多爽。
  • 序列化效率:Protobuf 是二进制格式,体积只有 JSON 的 1/3 到 1/5。解析速度也快一个数量级。
  • 强类型:proto 文件定义了严格的字段类型和结构。接口迁移时,字段改没改,编译阶段就能发现。
核心观点: gRPC 不适合浏览器直连。浏览器原生不支持 HTTP/2 的 gRPC 帧格式。必须用 gRPC-Web 做代理,或者用 Envoy 做转码。

1.3 WebSocket:全双工的实时通道

WebSocket 是 HTML5 推出的。它解决了一个痛点:服务端主动推送数据。

  • 一次握手,长期连接:客户端发一个 Upgrade 请求,服务端同意后,连接就建立起来了。之后双方都能随时发数据。
  • 消息即发即收:没有请求-响应的绑定关系。服务端可以主动推送行情、通知、日志。
  • 传输协议:数据可以是文本或二进制。框架会帮你做好帧的拆分和重组。
  • 连接管理:心跳、重连、并发限制,这些都需要自己在 Client 层面处理。
避坑指南: 我曾经在一个金融行情项目里,WebSocket 连接数一多,服务端 CPU 直接跑满。排查下来,是每条消息都做了全量 JSON 序列化。解决方案是:把高频数据用 Protobuf 编码,只在连接建立时协商一次格式。后来连接量翻了 3 倍,CPU 才涨了 30%。

1.4 GraphQL:前端驱动的数据查询

GraphQL 是 Facebook 搞的。它让你用一次请求,拿到刚刚好的数据。不多不少。

  • 声明式查询:前端在 Query 里写清楚要哪些字段。服务端只返回这些字段。没有 Over-fetching 和 Under-fetching。
  • 单一端点:所有数据都走 /graphql。后端不需要为每个页面写单独的 API。
  • 强类型 Schema:类型系统定义了所有对象、字段、参数。前端可以在开发时就发现字段不存在的问题。
  • 解析器组合:一个字段可以映射到不同的数据源。比如用户信息查关系数据库,用户头像查对象存储。

1.5 协议格式对比:一张表说清楚

对比维度 HTTP (REST) gRPC WebSocket GraphQL
传输格式 文本 (JSON/XML) 二进制 (Protobuf) 文本/二进制 文本 (Query + JSON)
通信模式 请求-响应 一元/流式/双向 全双工 请求-响应(支持批量)
数据体积 大(纯文本 + Header) 小(二进制 + 压缩) 取决于数据格式 中等(查询语句额外占用)
解析速度 中 快 取决于数据格式 中(需解析查询 AST)
浏览器原生支持 完全支持 不支持(需 gRPC-Web) 完全支持 完全支持
学习曲线 低 中高(Proto 语法、工具链) 低 中(Schema 设计、N+1 问题)

1.6 适用场景分析:什么时候选哪个?

我一般这样判断:

  • HTTP(RESTful):CRUD 为主的业务系统、公开 API、要与浏览器或移动端交互。只要是请求-响应模式,对性能要求不那么极致,优先选它。生态最完善,调试工具最多。
  • gRPC:微服务间通信、高频数据交换、IoT 设备间同步、需要流式处理的场景(比如订阅股票实时行情)。我个人习惯,内部服务间通信一律用 gRPC,效率高太多了。
  • WebSocket:实时性要求高的场景:在线聊天、游戏帧同步、协作编辑、股票行情推送。它的核心优势是“服务端能主动开口”。
  • GraphQL:前后端分离、前端对数据灵活性要求高、后端有多个异构数据源(MySQL + Redis + ES + 第三方 API)。适合数据聚合层。
一条经验法则: 选择协议时,先问自己三个问题——1)通信是一问一答还是服务端主动推?2)数据量大不大,对体积敏感吗?3)客户端能控制 Proto 版本吗?想清楚这些,选型就不难了。
接口协议选型决策流程 确定业务场景 通信模式:一问一答 or 服务端主动推? 一问一答 服务端主动推 数据格式灵活? 是 GraphQL 否 HTTP REST 实时性要求高? 是 WebSocket 否 gRPC 选型提醒 内部服务间:优先 gRPC(性能好、强类型) 对外 API:优先 REST(生态好、调试方便) 实时推送:优先 WebSocket(浏览器原生支持)

1.7 我的迁移经验:从 HTTP 到 gRPC,踩过的坑

我曾参与过一个迁移项目。旧系统全是 REST API,服务间调用靠 JSON 来回传。接口文档写了一大堆,可是客户端和服务端总对不上字段。

我们决定把内部服务切到 gRPC。过程并不顺利。

  • 坑一: Proto 文件版本管理没做好。A 服务升级了 proto,B 服务没同步,编译直接崩。后来我们把 proto 放独立 repo,CI 里加 proto 版本检查。
  • 坑二: 双向流式调用的超时设置。我记得有一次,双向流通道没设 idle_timeout,连接一直挂着,内存泄漏了两个月才发现。
  • 坑三: 旧系统的 JSON 日志直接解析不了 gRPC 二进制帧。日志采集管道全部要改。

效果呢?迁移完成后,服务间通信延迟平均降了 40%,带宽使用只有原来的 1/5。虽然过程痛苦,但结果确实值。

一个小技巧: 如果觉得 Protobuf 的 .proto 文件写起来麻烦,可以考虑用 buf 工具链。validate、lint、breaking change check 一条命令搞定。我个人习惯,每次迁移前先用 buf breaking 跑一次,确认旧接口没被改坏。