一、接口协议基础:四个常用的协议,你真正用过几个?
做接口迁移,首先得把协议本身吃透。我见过不少团队,一上来就选 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 版本吗?想清楚这些,选型就不难了。
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 跑一次,确认旧接口没被改坏。