摘要
微服务架构的本质,只分为两大模块:服务创建 + 服务治理 。Spring Boot 解决"如何快速创建应用 ",而 Spring Cloud、Dubbo 是两套截然不同的微服务治理体系 。Spring Cloud 是积木式组件体系,主打 HTTP REST;Dubbo 是一体化 RPC 框架,依托强契约接口做服务定义,内置全套治理能力。企业落地的经典架构思想:对外 REST 求兼容,对内 RPC 求性能;南北走 REST,东西走 RPC。本文从底层原理、流量分层、通信模型,结合企业落地实践,对比两套体系的差异,重点从连接模型、协议设计、序列化、线程模型、调用路径到设计取舍,由表及里六层拆解 RPC 高性能的完整因果链,给出落地选型判断依据。
一、微服务的本质:两件事------服务创建 + 服务治理
服务创建:Spring Boot 作为底座
Spring Boot 核心思想是约定大于配置,自动配置、内嵌容器,大量减少 XML、繁杂 Bean 配置,让开发者快速搭建出独立可运行的 Java 应用。
当应用数量变多,几十个、上百个服务部署运行之后,就会产生新的诉求:服务在哪、能不能调用、负载怎么分配、调用失败如何处理、流量灰度怎么控制。这部分就是服务治理。
服务治理有两套主流落地体系:Spring Cloud 和 Dubbo。
| 对比项 | Spring Cloud | Dubbo |
|---|---|---|
| 定位 | 积木式组件拼装,按需引入 | 一体化 RPC 框架,原生内置全套治理 |
| 服务形态 | HTTP REST(面向资源 URL) | 强契约接口模型(面向接口方法) |
| 通信模型 | HTTP,每次请求响应语义独立,无单连接多路复用 | TCP 长连接,多路复用 |
| 序列化方案 | JSON 文本 | 二进制(Hessian2/Protobuf),插件化可替换 |
| 治理能力 | 按需选配注册中心、负载均衡、限流组件 | 注册发现、负载均衡、集群容错、路由全部开箱即用 |
| 分组与版本 | 无原生分组能力,依靠 metadata 自定义扩展实现 | 原生内置 Group / Version,直接用于服务分组、版本隔离 |
| 契约强弱 | 弱契约,依靠接口文档维护,消费端不需要共享代码 Jar 包 | 强契约,消费方必须引入接口 Jar 包,接口作为通信契约 |
| 跨语言 | HTTP 协议天然跨语言,任意语言均可接入 | 以 Java 生态优先,跨语言体验偏弱 |
| 调试手段 | HTTP 可直接使用 curl、Postman 调试,简单方便 | 私有 RPC 协议,无法直接 curl 调试,依赖 Dubbo 专用管理工具 |
Dubbo 同样构建在 Spring Boot 之上,但它改变了服务对外暴露的形态。
Spring Cloud 生态里,服务是以 HTTP 的 GET/POST 接口对外提供;而 Dubbo 不是 HTTP 接口,它采用**IDL 接口定义语言**,在服务提供者与消费者之间提前约定接口、方法、入参、返回值,形成双方都必须遵守的强契约。
- 服务提供者 :使用
@DubboService注解,将接口实现类发布为 RPC 服务; - 服务消费者 :使用
@DubboReference注入远程服务代理,调用远程接口就像调用本地普通 Java 方法。
二、RPC 高性能的本质
在内网服务调用场景,Dubbo、gRPC 这类 RPC 框架,相比 Spring Cloud Feign 的 HTTP 调用,拥有更低的延迟、更高的吞吐。
RPC 高性能的本质,不是某一项技术优势,而是一整套"为内网 Java 调用量身定制"的设计取舍:放弃跨语言通用性,换取紧凑协议与二进制序列化;绕过 Servlet 容器,缩短调用路径;复用 TCP 连接,减少握手开销。每一层都在做减法,减到最后就是性能。
这套分层优化体系覆盖网络连接、协议编码、对象序列化等全链路。本节由底层向上逐层拆解 RPC 高性能的全链路因果逻辑。
2.1 连接模型(TCP 长连接 + 多路复用)
Spring Cloud REST 基于 HTTP 协议(常用 HTTP/1.1),每次请求-响应语义独立,无法在单连接上并发复用多个请求;相比 Dubbo 的 TCP 多路复用,仍存在连接建立的开销。
HTTP/1.1 虽然支持 Keep-Alive 保持长连接,但一条连接同一时刻只能串行处理一组请求响应。高并发场景下,需要创建大量连接,带来 TCP 握手、操作系统连接队列、文件句柄等额外开销。
Dubbo 底层直接基于TCP 长连接 ,一次握手建立连接之后可以长期复用。并且 Dubbo 实现了连接多路复用:一条 TCP 连接上,可以并发传输多个 RPC 请求,请求和响应依靠请求 ID 一一对应,不需要为每一次方法调用新建一条 TCP 连接。
性能收益:
- 省去反复创建、销毁 TCP 连接带来的三次握手/四次挥手开销;
- 单连接承载大量并发请求,大幅减少操作系统文件句柄、连接资源占用。
2.2 协议设计(自定义紧凑二进制协议)
HTTP 属于文本协议,报文自带大量冗余 Header:Cookie、Accept、User-Agent 等文本头,每次请求重复携带,报文体积大,包含大量换行、空格、分隔符这类非业务冗余字符。
Dubbo 使用自研紧凑二进制协议,协议头极简,只保留核心字段:请求 ID、版本号、序列化类型、消息长度等,没有 HTTP 协议的文本冗余。
性能收益:
- 报文头部体积极小,减少网络传输的数据量;
- 二进制帧结构便于机器解析,不需要文本词法分割,降低编解码开销。
2.3 序列化(二进制编码)
Spring Cloud REST 默认使用 JSON 序列化,属于纯文本格式。JSON 会把所有数据全部转为字符串存储:数字、布尔、对象全部变成文本字符,依靠引号、逗号、大括号、冒号做结构分割。
例如数字 123,在 JSON 中是以字符串 "123" 存储,而非原生二进制数字,存在空间浪费与解析开销。
JSON 文本序列化三大短板:
- 报文体积大:大量分隔符、引号、键名重复传输,网络流量开销高;
- CPU 开销高:每次调用需要完整词法解析、字符串拆分、类型转换,解析耗时严重;
- 类型不安全:文本丢失原生类型信息,反序列化需要大量类型推断、兼容处理,容易出现类型异常。
Dubbo 支持二进制序列化 (默认 Hessian2,同时支持 Protobuf、Kryo 等),并且序列化与通信协议完全解耦,可插件化切换。
二进制序列化直接将 Java 对象按照二进制字节码写入网络流,不需要转文本、不需要分隔符,数字、布尔、对象类型直接以原生二进制存储。
二进制序列化性能收益:
- 序列化后字节体积极小,大幅降低网络传输耗时与流量开销;
- 无需文本词法解析,直接二进制编解码,CPU 消耗极低,序列化/反序列化速度远快于 JSON;
- 天然保留强类型信息,配合 Dubbo 接口强契约,类型校验精准、无转换异常。
2.4 线程模型层:IO 线程与业务线程池隔离
Dubbo 底层通信基于 Netty,采用标准 Reactor 线程模型,IO 线程和业务线程池做了彻底隔离。
IO 线程(EventLoop)只负责网络字节的读写、编解码,不执行业务逻辑,保证 IO 线程不会被阻塞;反序列化完成后,请求会投递到独立业务线程池执行业务代码。
对比传统 Servlet 模型(Tomcat):采用一请求一线程模型,一个请求绑定一条独立线程。如果业务代码发生 DB、Redis 这类阻塞 IO,线程会被卡住,无法处理其他请求,高并发下容易耗尽线程资源。
即便使用 WebFlux 异步非阻塞,HTTP 请求从进入容器到返回响应,依然要走完完整 Filter 链、DispatcherServlet 调度,存在固定调度开销。
Dubbo 这套线程隔离设计,保证网络 IO 不会被业务阻塞,高并发场景下线程切换开销更低,资源利用率更高。
Dubbo 默认配置:Boss 线程数 = 1,Worker(IO)线程数 = CPU 核数 × 2,业务线程池默认 200 个线程(可通过
threads参数调整)。三层线程各司其职,互不阻塞。
2.5 调用路径层:接口代理,精简调用链路
Spring Cloud Feign 的一次完整调用链路:
Feign 代理 → HTTP 客户端 → 建立/复用连接 → 组装 HTTP 报文 → JSON 序列化 → 网络传输 → Tomcat 接收 → HTTP 解码 → DispatcherServlet 路由 → Controller 参数解析 → 调用 Service
Dubbo 的一次 RPC 调用链路:
DubboReference 代理 → 负载均衡选实例 → Netty 发送二进制帧 → 网络传输 → Netty IO 线程读取帧 → 反序列化 → 直接调用接口实现方法
Dubbo 基于接口代理做远程调用,直接绕过 Servlet 容器、Filter 拦截器链、HTTP URL 路由、Controller 参数绑定整套 HTTP 生命周期,调用链路大幅缩短,减少了大量中间调度开销。
2.6 设计取舍层:放弃通用性,换取内网调用极致性能
这是 RPC 高性能最根本的原因。
HTTP REST 从设计之初就要兼容浏览器、移动端、多语言客户端,通用性是第一目标,为了通用不得不承担额外开销,也就是通用性性能税。
而 Dubbo 这类 RPC 框架,设计目标非常聚焦:服务内网 Java 服务之间高频调用。
基于这个前提,它主动做了一系列取舍:
- 不需要兼容浏览器,不必遵守 HTTP 全套语义;
- 不优先跨语言,可使用 Java 友好的二进制序列化;
- 报文不需要人类可读,协议头可以做到极简紧凑;
- 不需要通用容器,可绕过 Servlet,直接做接口方法调用。
所有的性能优化,都建立在这一场景取舍之上。不是单纯技术更强,而是场景更专一。
2.7 综合性能总结
HTTP + JSON(Spring Cloud):文本协议、单连接无法多路复用、报文头部冗余严重、文本解析 CPU 开销大。核心优势是通用、跨语言、可调试,为了兼容浏览器、多语言、各类中间件,牺牲了性能,自带「通用性性能税」,天生不适合内网高频调用。
TCP + 二进制 RPC(Dubbo):长连接复用、单连接多路并发、极简二进制协议头、高效二进制编解码,全方位减少网络 IO 与 CPU 开销,在内网高频服务调用场景,实现低延迟、高吞吐。
终极架构结论:
HTTP 是「为通用兼容而生」,RPC 是「为内网性能而生」。对外 REST 求兼容,对内 RPC 求性能,不是技术选型的偏好,是架构设计取舍的必然结果。表层的协议、序列化差异,只是底层整套架构设计优势的最终体现。
RPC 高性能的完整因果链(由表及里)
| 层次 | 做了什么 | 性能收益 |
|---|---|---|
| ① 序列化层 | 二进制替代文本 JSON | 体积更小、CPU 解析更快 |
| ② 协议层 | 自定义紧凑二进制协议替代 HTTP 文本协议 | 去掉冗余 Header,报文头部极简 |
| ③ 连接层 | TCP 长连接 + 多路复用替代 HTTP 短连接/独立连接 | 省去反复握手开销,单连接并发承载 |
| ④ 线程模型层 | IO 线程与业务线程池隔离(Netty Reactor 模型) | 避免阻塞 IO 线程,高并发下线程切换少 |
| ⑤ 调用模型层 | 接口代理 + 方法级调用,无需完整 HTTP 请求生命周期 | 跳过 Servlet 容器 Filter 链、HTTP 编解码全流程 |
| ⑥ 设计取舍层 | 放弃通用性(跨语言、浏览器兼容),换取极致性能 | 所有优化都源于"只服务内网 Java 调用"这个前提 |
三、服务调用容错策略:Dubbo VS Spring Cloud
3.1 Dubbo 消费端集群容错策略
Dubbo 的容错逻辑内置在框架消费端,负载均衡选中实例、发起网络调用失败后,执行对应的集群容错策略,一共 6 种:
- Failfast(快速失败):调用一次失败,直接抛出异常,不重试。适合写接口、事务接口,不能重复提交。
- Failsafe(失败安全):捕获并吞没异常,不向上抛出,直接返回空。适合埋点、日志上报这类非核心逻辑,允许丢数据,不能影响主业务。
- Failback(失败自动恢复):调用失败,立刻给客户端返回成功,不阻塞业务;后台线程异步排队重试。适合消息通知、事件推送,允许延迟送达。
- Failover(故障转移,Dubbo 默认策略) :当前节点调用失败,不重试当前节点,自动切换集群内其他可用实例重试 。适合查询读接口;️ 必须保证接口幂等,写接口严禁使用,会产生重复写。
- Forking(并行调用):并行同时请求多个实例,任意一个成功就返回结果,适合对延迟极其敏感的查询场景。
- Broadcast(广播调用):广播调用所有服务实例,任意一个调用失败,整体判定失败,多用于批量配置更新。
Dubbo 执行链路: Consumer Filter 链 → Cluster 集群容错 → LoadBalance 负载均衡 → 选定实例 → Netty 发起 RPC 网络调用
3.2 Spring Cloud 容错策略(Sentinel / Resilience4j)
Spring Cloud 本身没有内置容错组件,属于按需引入,主流使用 Resilience4j 或者 Sentinel 实现容错,核心能力包含重试、熔断、降级、舱壁:
- 重试(Retry) :调用失败自动重试,可以配置重试次数、间隔、异常类型。和 Dubbo Failover 类似,但基于 HTTP 调用,重试会重新走完整 HTTP 请求流程。读接口使用,写接口必须谨慎防止重复提交。
- 熔断(CircuitBreaker):当下游失败率超过阈值,打开熔断器,直接快速返回兜底结果,不再发起远程调用,保护下游服务雪崩。
- 降级(Fallback):调用异常、超时、熔断触发时,执行预先定义的兜底方法,返回预设结果。可以返回默认值、缓存数据。
- 超时(TimeLimiter):限制远程 HTTP 调用最大等待时长,超时直接终止请求,避免线程长时间阻塞。
- 舱壁(Bulkhead):隔离线程池,为不同服务调用分配独立线程资源,单个下游故障不会耗尽整个应用线程池,防止服务雪崩。
Spring Cloud 调用链路: Feign 拦截器 → Resilience4j/Sentinel 容错组件 → 负载均衡 → HTTP 请求调用远端服务
3.3 两者容错的核心差异
- 内置形态:Dubbo 集群容错原生内置框架;Spring Cloud 容错能力需要额外引入 Resilience4j/Sentinel 组件,按需装配。
- 生效位置:Dubbo 容错是 RPC 集群层,在实例选择环节做节点切换;Spring Cloud 容错作用在 HTTP 客户端(Feign)之上。
- 熔断能力:Dubbo 3.x 已提供 CircuitBreaker 扩展支持,但生态成熟度和开箱体验不如 Spring Cloud + Sentinel 组合;Spring Cloud 生态天然将熔断降级作为核心容错能力,是应对雪崩的首选方案。
- 模型侧重点 :Dubbo 集群容错更多解决实例调用失败后的路由重试 ;Spring Cloud 生态容错重点在于防止服务雪崩、流量降级保护。
四、流量分层最佳实践:南北 REST,东西 RPC
大规模微服务落地,最经典的架构分层就是区分南北流量 与东西流量 ,也就是南北 REST、东西 RPC,两套协议各司其职,不要混用。
| 对比项 | 南北流量(外部 内部) | 东西流量(内部 内部) |
|---|---|---|
| 协议 | REST / HTTP + JSON | TCP/RPC + 二进制序列化(Dubbo/gRPC) |
| 完整链路 | 客户端 → Nginx 流量网关 → API Gateway 业务网关 → 后端微服务 | 消费端直连提供端,不经过网关转发 |
| 核心目标 | 兼容性、安全性、可调试 | 高性能、低延迟、强治理能力 |
| 网关分工 | 流量网关(Nginx):SSL、WAF、全局限流;业务网关(Spring Cloud Gateway):流量染色、Header 标签透传、灰度路由、鉴权 | 路由、负载均衡、实例过滤、灰度逻辑,在消费端本地执行 |
| 标签透传 | HTTP Header 透传,网关入口统一打标 | Dubbo Attachment 透传,可利用 Group/Version 实现服务分组隔离 |
南北流量:外部客户端(浏览器、APP、第三方系统)访问内部业务,是系统出入口。外部客户端只认识 HTTP 协议,所以统一使用 REST + JSON。依靠网关统一做安全防护、鉴权、流量管控,同时 HTTP 支持 curl、Postman,接口调试便捷,优先保证兼容性与安全性。
东西流量:微服务和微服务之间的内部调用,属于内网闭环通信,没有跨语言强制要求。选用 Dubbo 这类 RPC 框架,基于 TCP 长连接 + 二进制序列化,服务之间点对点直连,省去网关转发损耗,追求高吞吐、低延迟,同时依托框架内置能力做服务治理。
五、选型怎么选?落地判断标准
优先 Spring Cloud(HTTP REST)
- 对外网关接入,承接南北流量,面向浏览器、App、第三方系统对接;
- 多语言混合技术栈,需要跨语言调用;
- 团队技术栈熟悉 HTTP,业务以简单 CRUD 为主,没有极高吞吐、低延迟诉求;
- 希望组件解耦,可自由替换注册中心、网关等组件,快速调试接口。
优先 Dubbo(RPC)
- 服务集群内部大量东西向高频服务调用,追求高性能、低延迟;
- 需要原生的服务分组、版本隔离,做灰度、版本管控;
- 团队希望框架自带完整治理能力,不想自己组装一堆中间件;
- 强契约约束,接口版本管理严格,适合金融、企业级核心 Java 业务。
现实生产最佳架构 :二者可以完美共存,也就是对外 REST + 对内 RPC 架构。网关用 Spring Cloud Gateway 承接外部 HTTP 请求,服务内部互调用 Dubbo RPC,做到:南北流量求兼容、东西流量求性能。
六、全文总结金句
Spring Boot 负责造应用,Spring Cloud 负责拼装治理 HTTP 服务,Dubbo 负责用强契约 RPC 定义服务------全套治理能力原生内置,通信与序列化插件化。
流量网关看门,业务网关调度;对外 REST 求兼容,对内 RPC 求性能;南北走 REST,东西走 RPC。