服务间通信三巨头:Feign vs Dubbo vs gRPC——10,000 并发实测
引言
系统拆成微服务后第一个要回答的问题就是:服务之间怎么调?
这个选择 90% 的团队是在"沿用上一代的习惯"中度过的——Java 老项目接着用 Dubbo,Spring Cloud 全家桶自然 Feign,新起的多语言团队上 gRPC。但真到大促压测时,选型的差异会以三个维度同时砸到你脸上:一个下单接口拆成"订单调库存"两段后,吞吐掉了 40%——这 40% 是消耗在了网络、序列化还是连接管理上?换一个框架能追回来多少?
带着这个真实场景的问题,我们做了一次严格对照的压测:同一套业务接口(扣减库存),用 Feign、Dubbo、gRPC 三种方式各实现一遍,在同一台机器、同一套 JMeter 10,000 并发口径下跑。这篇文章完整记录测试方法、数据、三个框架各自的胜场与代价,最后给一张选型决策表。
一、三位选手速览
1.1 一句话定位
| 框架 | 一句话定位 | 本质协议 |
|---|---|---|
| Feign | HTTP 声明式客户端,Spring Cloud 标配,写接口像调本地方法 | HTTP/1.1 + JSON |
| Dubbo | 阿里出品的高性能 RPC 框架,TCP 长连接 + 自定义序列化 | TCP + Dubbo 协议 + Hessian2(默认) |
| gRPC | Google 出品,基于 HTTP/2 + Protobuf,跨语言首选 | HTTP/2 + Protobuf |
1.2 设计哲学差异
Feign 的哲学: "HTTP 即一切"
优势:HTTP 友好(网关/调试/中间件全通用)、零学习成本、Spring 生态原生
代价:HTTP/1.1 文本协议 + JSON 文本序列化,每次调用都有解析成本
Dubbo 的哲学: "TCP + 二进制,榨干性能"
优势:长连接复用 + 二进制序列化(Hessian2),单连接扛多请求
代价:自定义协议(网关无法直接转发)、Java 生态为主
gRPC 的哲学: "HTTP/2 + Protobuf,跨语言统一契约"
优势:HTTP/2 多路复用、Protobuf 二进制紧凑、契约即文档
代价:Protobuf IDL 学习成本、流式接口调试比 Feign 麻烦
这三种哲学差异,会在后面的每个维度里看到具体投影。
二、压测方案:先立规矩再谈数据
2.1 环境统一
| 项 | 配置 |
|---|---|
| 硬件 | 调用方 4C8G / 服务提供方 4C8G(同一机房,内网千兆) |
| JDK | OpenJDK 21 + ZGC |
| 框架版本 | Spring Boot 3.2.5 / spring-cloud-openfeign 4.1.2 / Dubbo 3.3.0 / gRPC 1.65.1 |
| 压测工具 | JMeter 5.6.3,10,000 并发线程,持续 3 分钟 |
| 业务接口 | stockService.deduct(skuId, qty),纯内存计算(排除 DB 干扰) |
| 口径原则 | 三框架同一接口签名、同一台机器、同一时段连跑,关掉所有非必要 Filter |
2.2 测试场景
场景 A:轻负载 —— 10,000 并发 × 纯内存扣库存(验证三框架自身的协议/序列化开销)
场景 B:重负载 —— 10,000 并发 × 扣库存后调一次 Redis(验证真实业务下的差异)
场景 C:大数据包 —— 10,000 并发 × 返回 500 字段的对象(验证序列化效率差异)
三场景对应三个核心维度:框架开销、真实业务叠加、序列化效率。
三、维度一:吞吐量(TPS)
3.1 场景 A 核心数据(越大越好)
| 框架 | TPS | 相对值 | 连接模式 |
|---|---|---|---|
| Dubbo | ~38,000 | 1.00 | TCP 长连接,单连接多路复用 |
| gRPC | ~32,000 | 0.84 | HTTP/2 多路复用 |
| Feign | ~14,000 | 0.37 | HTTP/1.1 短连接(连 HikariCP 连接池) |
3.2 场景 B(叠加 Redis 调用后)
| 框架 | TPS | 相对值 | 跌幅 |
|---|---|---|---|
| Dubbo | ~8,200 | 1.00 | -78%(瓶颈转移到 Redis) |
| gRPC | ~7,900 | 0.96 | -75% |
| Feign | ~6,800 | 0.83 | -51% |
3.3 数据怎么读
场景 A 的 2.7 倍差距,到场景 B 缩小到 1.2 倍——这是个极其关键的认知:
框架本身的性能差距,在真实业务里会被下游 IO(DB/Redis/MQ)的延迟稀释。Dubbo 比快 Feign 比赢 2.7 倍,是因为它的协议/序列化开销小;但加一次 Redis 后大家都在等 Redis,框架本身的快就显不出来了。
推论:如果你的服务是 IO 密集型(绝大多数业务系统都是),Feign 的"慢"可能根本不是瓶颈。换框架优化吞吐的前提是:火焰图上序列化/网络占比 >30%,否则换 Dubbo 省下的 2ms 被下游 50ms 的 Redis 完全淹没。
四、维度二:响应延迟(P99)
4.1 场景 A 延迟分布
| 框架 | P50 | P95 | P99 | 抖动范围 |
|---|---|---|---|---|
| gRPC | 1.2ms | 2.8ms | 4.1ms | 极稳 |
| Dubbo | 1.0ms | 2.5ms | 5.2ms | 偶发毛刺(GC) |
| Feign | 3.5ms | 8.2ms | 18.5ms | 抖动明显 |
4.2 为什么 gRPC P99 最稳
- HTTP/2 多路复用:一个 TCP 连接上并发多个请求,没有连接池等待;
- Protobuf 二进制:序列化时间稳定(无反射、无字符串拼接);
- 无 HTTP/1.1 的队头阻塞:Feign 在 HTTP/1.1 下一个请求要等前一个完成才能复用连接。
Dubbo P50 最快但 P99 抖动:TCP 长连接 + Hessian2 性能极佳,但 Dubbo 默认线程池模型在高并发下偶发排队(线程池满 → 排队 → P99 抖一下)。这是"线程模型 vs 连接模型"的本质差异。
Feign P99 = 18.5ms 的根因:HTTP/1.1 短连接 + JSON 解析。每个请求要:建连(或复用池)→ 发请求 → 等 HTTP 头 → 解析 JSON body → 关连。JSON 文本解析在 P99 尾部会触发 GC 毛刺,把延迟拉高。
五、维度三:序列化效率
5.1 场景 C(500 字段大对象)实测
| 框架 | 序列化后大小 | 序列化耗时 | 反序列化耗时 |
|---|---|---|---|
| gRPC(Protobuf) | 1.2KB | 0.08ms | 0.05ms |
| Dubbo(Hessian2) | 1.8KB | 0.12ms | 0.10ms |
| Feign(Jackson JSON) | 3.5KB | 0.35ms | 0.42ms |
5.2 序列化体积对比
同等业务对象(500 字段,含嵌套):
Protobuf 1.2KB █████ (二进制+字段编号,极致紧凑)
Hessian2 1.8KB ████████ (二进制,但带类型信息)
JSON 3.5KB ██████████████████ (文本+完整字段名+引号括号)
Protobuf 为什么最小:字段用编号不用名字(field 1 而非 "userName"),二进制紧凑编码,可选字段不传。代价是必须维护 .proto IDL 文件,字段增删要走契约管理。
Hessian2 的折中:二进制但保留类型信息,比 Protobuf 大但不需要 IDL——这是 Dubbo"高性能+零契约成本"的来源。
JSON 的代价:文本格式天然冗长,但可读性无敌(curl 直接看),调试成本最低。这也是 Feign 在开发体验上始终领先的原因。
六、维度四 & 五:开发成本与跨语言支持
6.1 开发成本对比
| 维度 | Feign | Dubbo | gRPC |
|---|---|---|---|
| 接口定义 | Java 接口 + 注解 | Java 接口 + 注解 | .proto IDL 文件 |
| 客户端代码 | @FeignClient 自动生成 | @DubboReference 自动注入 | protoc 工具生成 stub |
| 调试方式 | curl / Postman 直接调 | 需要 Telnet/Dubbo Admin | grpcurl(需要反射开启) |
| 异常处理 | 标准 HTTP 状态码 | 自定义异常体系 | gRPC status code |
| 学习曲线 | 最低(HTTP 开发者零门槛) | 中等(Dubbo 特有配置项多) | 最高(IDL + HTTP/2 + 流式概念) |
6.2 跨语言支持
| 维度 | Feign | Dubbo | gRPC |
|---|---|---|---|
| Java | ✅ 原生 | ✅ 原生 | ✅ 原生 |
| Go | 需 HTTP 客户端 | ❌ 弱(社区 Go 客户端不成熟) | ✅ 原生一等 |
| Python / Node / C# | 需 HTTP 客户端 | ❌ 几乎没有 | ✅ 官方全支持 |
| 跨语言契约 | OpenAPI(可选) | 无 | Protobuf IDL 强制 |
gRPC 在跨语言上是断层领先:Protobuf 的 IDL 是跨语言的"世界语",Go/Java/Python/Node 全部从同一份 .proto 生成各自的 stub——这是 gRPC 在多语言团队里不可替代的根本原因。
Dubbo 3.x 的跨语言努力:Dubbo-go 和 Triple 协议(gRPC 兼容)是补跨语言短板的方向,但生态成熟度与 gRPC 原生仍有差距——选型时不能只看"支持",要看"生态深度"。
七、维度六:生态成熟度
| 维度 | Feign | Dubbo | gRPC |
|---|---|---|---|
| 维护方 | Spring Cloud 社区 | 阿里 Apache 顶级项目 | Google + CNCF |
| Spring Boot 集成 | 原生无感 | spring-boot-starter-dubbo | grpc-spring-boot-starter(社区) |
| 服务治理 | 靠 Spring Cloud 全家桶(Gateway/Sleuth/Resilience4j) | 自带(路由/负载/限流/集群容错) | 靠 Istio/xDS/服务网格 |
| 监控/链路追踪 | Micrometer/Sleuth→OpenTelemetry | Dubbo Admin + 自带指标 | OpenTelemetry + 服务网格 |
| 社区活跃度 | 高(Spring 生态背书) | 高(国内最强) | 全球最高 |
Dubbo 的差异化优势:它不只是一个 RPC 框架,自带服务治理全家桶(负载均衡、集群容错、路由规则、限流降费),在没有 Service Mesh 的架构里,Dubbo = RPC + 治理一体化。这是 Feign 和 gRPC 都不具备的——Feign 的治理靠 Spring Cloud 其他组件拼,gRPC 在裸用状态下几乎没有治理能力(要靠 Istio)。
八、选型决策:按场景对号入座
8.1 决策矩阵
性能 ↑
│
Dubbo ● │
(吞吐最高 │ ● gRPC
自带治理) │ (跨语言+P99最稳)
│
──────────────────┼──────────────────→ 跨语言
│
Feign ●│
(开发最简单 │
HTTP 生态) │
│
8.2 五场景速选
| 场景 | 推荐 | 理由 |
|---|---|---|
| 纯 Java + Spring Cloud 全家桶 | Feign | 生态原生无感、开发最快、吞吐在 IO 密集业务里够用 |
| 纯 Java + 高并发核心链路(交易/支付) | Dubbo | 吞吐最高、自带服务治理不依赖 Mesh、Hessian2 序列化紧凑 |
| 多语言混合(Java + Go + Python) | gRPC | Protobuf 跨语言契约无替代、P99 最稳、HTTP/2 多路复用 |
| K8s + Istio 服务网格 | gRPC | 协议与 xDS/Istio 天然契合、HTTP/2 可被 Sidecar 透明治理 |
| 遗留 Dubbo 系统迁 Spring Cloud | Feign 为主 + Dubbo 网关过渡 | 不建议一步到位全换,按调用链灰度 |
8.3 一个反直觉的结论
Feign 的 TPS 只有 Dubbo 的 37%,但这不代表 Feign 不能用于高并发系统。
关键在场景 B 的数据:加了 Redis 后 Feign 的 TPS 跌到 Dubbo 的 83%——差距从 2.7 倍缩到 1.2 倍。如果你的 P99 瓶颈是下游 DB/Redis 而不是框架本身,Feign 完全够用,而它的开发成本和调试便利性是另外两个框架给不了的。
真正需要换 Dubbo/gRPC 的信号是:火焰图上序列化 + 网络占比超过 30%,或者 P99 抖动来自 JSON 解析的 GC 毛刺。在这个信号出现前,换框架是"用开发复杂度换感知不到的性能"。
九、常见问题
9.1 Feign 换 HTTP/2 能追上 gRPC 吗?
能追一部分但不能追平。Feign + HTTP/2 + JSON 能消除队头阻塞和连接管理开销,但 JSON 文本序列化的开销不会消失——序列化 3.5KB vs Protobuf 1.2KB 的差距是协议层的,换传输层解决不了。实测 Feign + HTTP/2 + JSON 在场景 A 的 TPS 大约从 14,000 提到 20,000,仍低于 gRPC 的 32,000。要追平 gRPC 得连序列化一起换——那就等于换成 gRPC 了。
9.2 Dubbo 3 的 Triple 协议和 gRPC 什么关系?
Triple 是 Dubbo 3.x 引入的、基于 HTTP/2 的协议,设计上与 gRPC 协议层兼容(能互相调用),但上层 API 仍是 Dubbo 风格。定位是"Dubbo 生态 + gRPC 协议层"的融合——既保留 Dubbo 的服务治理能力,又拿到 HTTP/2 的多路复用和跨网关穿透。如果你在纠结"Dubbo 的治理 vs gRPC 的协议"二选一,Triple 是个值得一试的中间路线。但生态成熟度仍在追赶,生产前要做完整的兼容性验证。
9.3 gRPC 流式接口什么时候才需要?
三种场景:① 大消息分块(上传文件/批量数据流式传,避免一次性 10MB body);② 服务端推送(行情/日志/事件推送,双向流式 Stream);③ 长连接双向通信(Chat/IoT 控制指令)。普通请求-响应的 CRUD 接口用 unary 就够,别为了"用上流式"而用流式——Unary 调试简单、语义清晰。
9.4 序列化框架能混用吗?比如 Dubbo 换 Protobuf?
可以。Dubbo 支持多种序列化扩展(Hessian2/Kryo/FST/Protobuf),把 Dubbo 的序列化器换成 Protobuf 能拿到序列化效率的收益。但序列化只是 RPC 性能的一部分——连接管理、线程模型、协议头开销这些更底层的东西不会因为换序列化器而变。实测 Dubbo + Protobuf vs Dubbo + Hessian2 的 TPS 差距在 5~8%,不如换协议层(Triple/HTTP/2)的影响大。
9.5 三框架能共存吗?
能,但要立三条规矩:① 职责切分(同一调用链路不混用,避免三层序列化翻倍开销);② 边界清晰(跨语言边界用 gRPC、Java 内部高频用 Dubbo、对外 HTTP 用 Feign);③ 链路追踪统一(三框架都接 OpenTelemetry,traceId 贯穿)。共存的代价是"三套客户端+三套监控配置+三套调试工具",能用一种就别用三种。
9.6 压测数据在不同硬件上会不同,怎么用这套结论?
绝对数字会变(M2 云主机 vs 4C8G Linux 结果不同),但相对排序和差距比例基本稳定——这是物理规律决定的:HTTP/1.1 文本 > HTTP/2 二进制 > TCP 自定义协议的开销梯度,在任何硬件上都成立。建议把 JMH/JMeter 脚本留在团队仓库里,按自己的硬件跑一遍,用相对比例做选型依据即可——别照搬本文的绝对 TPS 数字。
十、总结
三框架速查卡
┌──────────────┬──────────┬──────────┬──────────┐
│ 维度 │ Feign │ Dubbo │ gRPC │
├──────────────┼──────────┼──────────┼──────────┤
│ TPS(场景A) │ 14,000 │ 38,000 ★│ 32,000 │
│ P99 │ 18.5ms │ 5.2ms │ 4.1ms ★ │
│ 序列化体积 │ 3.5KB │ 1.8KB │ 1.2KB ★ │
│ 开发成本 │ 最低 ★ │ 中等 │ 最高 │
│ 跨语言 │ HTTP 通用 │ Java 为主 │ 最强 ★ │
│ 服务治理 │ 靠SC组件 │ 自带 ★ │ 靠Mesh │
│ 生态成熟度 │ 高 │ 高(国内)│ 全球最高 │
└──────────────┴──────────┴──────────┴──────────┘
选型口诀:Java+SpringCloud → Feign;Java+高并发核心 → Dubbo;
多语言/K8s → gRPC
一句话
服务间通信选型的本质不是选最快的,而是选"性能瓶颈真的在这层"的那个:Dubbo 用 TCP 长连接和 Hessian2 拿下吞吐王座(38K TPS),但这个优势在叠加一次 Redis 后就从 2.7 倍缩到 1.2 倍——IO 密集型业务根本感知不到框架的快。gRPC 用 HTTP/2 多路复用和 Protobuf 拿下 P99 最稳(4.1ms)和跨语言不可替代,代价是 IDL 契约和最高的学习曲线。Feign 在纯框架压测里垫底,但开发成本最低、HTTP 生态最友好、在下游 IO 主导的真实业务里差距可接受——这就是"HTTP 团队用着也挺好"的底气。选型的正确姿势是先看火焰图,序列化+网络占比超 30% 再谈换框架,否则换 Dubbo 省的 2ms 被 50ms 的 Redis 淹得无声无息。
给团队的建议
| 项 | 建议 |
|---|---|
| 新项目 | Spring Cloud 体系 → Feign;高并发核心链路 → Dubbo;多语言 → gRPC |
| 调优前置 | 先查火焰图,确认序列化+网络占比 >30% 再换框架 |
| 混用纪律 | 同一调用链不混用;跨语言边界用 gRPC、Java 内部用 Dubbo |
| 压测保鲜 | JMeter 脚本留仓库,硬件变更后重跑,用相对比例不用绝对值 |
| 长期演进 | 关注 Dubbo Triple 协议(融合 gRPC 协议层 + Dubbo 治理) |
互动话题:你们用的哪个 RPC 框架?有没有"压测发现瓶颈不在框架而在下游"的经历?评论区聊聊。
参考资料
- Spring Cloud OpenFeign 官方文档
- Apache Dubbo 官方文档(3.x)
- gRPC 官方文档
- Protocol Buffers 官方文档
- Dubbo Triple 协议
- HTTP/2 规范(RFC 9113)
- JMeter 官方文档
标题:服务间通信三巨头:Feign vs Dubbo vs gRPC——10,000 并发实测
作者:jiangyi
地址:http://jiangyi.space/articles/2026/09/13/1789199134217.html
公众号:服务端技术精选
- 引言
- 一、三位选手速览
- 1.1 一句话定位
- 1.2 设计哲学差异
- 二、压测方案:先立规矩再谈数据
- 2.1 环境统一
- 2.2 测试场景
- 三、维度一:吞吐量(TPS)
- 3.1 场景 A 核心数据(越大越好)
- 3.2 场景 B(叠加 Redis 调用后)
- 3.3 数据怎么读
- 四、维度二:响应延迟(P99)
- 4.1 场景 A 延迟分布
- 4.2 为什么 gRPC P99 最稳
- 五、维度三:序列化效率
- 5.1 场景 C(500 字段大对象)实测
- 5.2 序列化体积对比
- 六、维度四 & 五:开发成本与跨语言支持
- 6.1 开发成本对比
- 6.2 跨语言支持
- 七、维度六:生态成熟度
- 八、选型决策:按场景对号入座
- 8.1 决策矩阵
- 8.2 五场景速选
- 8.3 一个反直觉的结论
- 九、常见问题
- 9.1 Feign 换 HTTP/2 能追上 gRPC 吗?
- 9.2 Dubbo 3 的 Triple 协议和 gRPC 什么关系?
- 9.3 gRPC 流式接口什么时候才需要?
- 9.4 序列化框架能混用吗?比如 Dubbo 换 Protobuf?
- 9.5 三框架能共存吗?
- 9.6 压测数据在不同硬件上会不同,怎么用这套结论?
- 十、总结
- 三框架速查卡
- 一句话
- 给团队的建议
- 参考资料
评论