从 TCP 到 RPC:彻底搞懂「HTTP 与 RPC用法区别」
刚入行时,我曾一度困惑:HTTP 用得好好的,为啥微服务之间偏要选 RPC?直到顺着网络协议的演进脉络梳理下来,才发现这不是「替代关系」,而是「场景适配」的选择。今天就把这些知识点整合起来,用松散的节奏带你吃透 HTTP 与 RPC 的核心逻辑。
一、先搞懂底层:TCP 的特性与痛点
要理解 HTTP 和 RPC,得先从它们的「地基」—— TCP 说起。
TCP 是 70 年代就诞生的传输层协议,核心特性很明确:
- 面向连接:通信前必须建立三次握手
- 可靠传输:丢包会重传,保证数据完整
- 基于字节流:数据像水流一样连续传输,没有天然边界
但「字节流无边界」恰恰是个大问题 —— 这就是 粘包 / 拆包 痛点。
举个生动的例子:
客户端发送了两句话:「夏洛特」和「特烦恼」,但 TCP 传输时可能会把它们拼成「夏洛特特烦恼」一起发,也可能拆成「夏洛」「特特烦恼」分开发。接收端根本不知道哪里是一句话的结束,哪里是另一句话的开始。
怎么解决?
答案是「自定义协议封装」:在 TCP 字节流之上,加上「消息头 + 消息体」的结构。比如用消息头里的「长度字段」告诉接收端:接下来多少字节是一个完整的消息。这也是 HTTP 和 RPC 协议的底层核心逻辑。
二、HTTP 与 RPC 的起源:谁先谁后?
很多人误以为 RPC 是「后起之秀」,其实恰恰相反:
- 70 年代:TCP 诞生,提供底层传输能力
- 80 年代:RPC 出现,解决「裸 TCP」的开发痛点(序列化、粘包、连接管理)
- 90 年代:HTTP 流行,专为浏览器与 Web 服务器通信设计
所以问题本质不是「有了 HTTP 为什么要 RPC」,而是「有了 RPC 为什么要 HTTP」—— 因为 HTTP 更通用、更开放,适合跨平台、跨设备的公开通信。
它们的本质都是「基于 TCP 的应用层协议」,只是设计目标不同:
- HTTP:面向「浏览器 + 公开服务」,追求通用、兼容、易理解
- RPC:面向「内部服务 + 远程调用」,追求高效、性能、简洁
三、HTTP 与 RPC 的核心区别:一张表看明白
| 对比维度 | HTTP | RPC |
|---|---|---|
| 服务发现 | 依赖 DNS 解析域名,默认 80/443 端口 | 依赖服务注册中心(Consul/Etcd/Nacos)或 CoreDNS |
| 连接管理 | HTTP/1.1 用 Keep-Alive 复用连接,高并发下有瓶颈 | 维护连接池,请求时取连接、用完放回,高效应对高并发 |
| 传输格式 | 文本格式(JSON/XML),Header 冗余多(User-Agent、Content-Type 等) | 二进制格式(Protobuf/Thrift),Header 精简,无冗余 |
| 设计目标 | 兼容浏览器,需处理跨域、Cookie、302 跳转等场景 | 专注内部服务调用,无需兼容浏览器,只追求性能 |
| 开发成本 | 低,无需客户端 SDK,直接用 Postman 调试 | 中,需引入框架 SDK,生成客户端代码,但调用像本地方法 |
四、适用场景:没有好坏,只有适合
技术选型的核心是「匹配场景」,两者的分工很明确:
✅ 选 HTTP 的场景
- 对外提供服务:比如前后端交互、给第三方开放 API
- 需求简单,无需追求极致性能:比如管理后台的接口调用
- 跨平台、跨设备通信:比如手机 App、小程序、网页都要调用的服务
✅ 选 RPC 的场景
- 内部微服务通信:比如电商系统的「订单服务」调用「库存服务」
- 高并发、低延迟需求:比如秒杀场景,需要快速响应
- 多语言混合开发:比如 Java 服务调用 Go 服务,RPC 跨语言能力更强
五、补充知识点:让理解更完整
上面是核心逻辑,下面这些补充内容能帮你应对实际开发中的问题:
1. 常见的 RPC 实现框架(落地必备)
RPC 是一种「思想」,实际开发需要用具体框架:
- Dubbo:阿里开源,Java 技术栈首选,支持多协议、多注册中心,生态成熟
- gRPC:Google 开源,基于 HTTP/2 + Protobuf,跨语言能力极强(支持 Java/Go/Python 等 10+ 语言)
- Thrift:Facebook 开源,支持多种序列化协议和传输方式,灵活度高
- Spring Cloud OpenFeign:基于 HTTP 的 RPC 风格框架,兼顾开发效率和性能,适合 Spring 技术栈
2. HTTP 协议演进:正在缩小与 RPC 的性能差距
之前说 HTTP 性能不如 RPC,主要针对 HTTP/1.1。但 HTTP 也在进化:
- HTTP/2:支持多路复用(一个连接并发处理多个请求)、头部压缩、二进制帧,性能提升 30%~50%,甚至成为 gRPC 的底层传输协议
- HTTP/3:基于 QUIC(UDP + TLS),解决了 TCP 队头阻塞问题,性能更优,未来可能让 HTTP 和 RPC 的边界更模糊
3. RPC 的核心组件与完整调用流程
RPC 框架之所以好用,是因为封装了复杂的底层逻辑,核心组件包括:
- 服务注册与发现:服务启动时注册地址,调用时查询地址
- 序列化 / 反序列化:把对象转二进制(如 Protobuf),或反之
- 网络传输:基于 TCP/HTTP/2 传输数据
- 负载均衡:把请求分发到不同服务实例(轮询、加权轮询等)
- 容错机制:超时重试、熔断、降级(避免服务雪崩)
完整调用流程(像调用本地方法一样简单):
- 客户端调用本地代理(Stub),比如
orderService.reduceStock() - Stub 把参数序列化,通过网络发送给服务端
- 服务端代理(Skeleton)接收请求,反序列化参数,调用实际的
reduceStock()方法 - 服务端把返回结果序列化,发送给客户端
- 客户端 Stub 反序列化结果,返回给调用者
4. 粘包 / 拆包的 3 种具体解决方法
之前提到「消息头 + 消息体」,这里补充常见方案:
- 固定长度法:每个消息长度固定,不足的补空格(简单但浪费空间)
- 分隔符法:用特殊字符(如换行符)分隔消息(需处理消息中包含分隔符的情况)
- 消息头 + 消息体法:消息头包含消息长度(如 4 字节表示长度),这是 RPC 最常用的方式(如 Dubbo 协议)
5. 技术选型的具体决策依据
除了场景,还可以参考这几点:
- 团队技术栈:Java 团队选 Dubbo,多语言团队选 gRPC
- 性能要求:QPS 百万级选 RPC,十万级以下选 HTTP 足够
- 开发效率:追求快速上线选 OpenFeign/HTTP,追求长期性能选 Dubbo/gRPC
- 维护成本:HTTP 无需额外依赖,RPC 需维护注册中心、SDK 版本
总结:不用纠结,按场景选就对了
HTTP 和 RPC 不是「竞争对手」,而是「互补搭档」:
- 对外通信、简单场景:用 HTTP,通用、省心
- 内部微服务、高并发场景:用 RPC,高效、强大
搞懂它们的底层逻辑和场景差异,下次技术选型时就不会迷茫啦~