概述
Feign 调一次服务要序列化一遍 JSON、走一遍 HTTP 栈;Dubbo 走的是二进制 RPC,快,但它自己那套生态又不如 SpringCloud 全。这一篇只回答两个问题:为什么要把两个框架放到一个工程里,以及整合之后到底长什么样。
纲要
- 立论:Feign + HTTP 的短板、Dubbo 的短板,各自缺什么
- 整合目标:治理归 SpringCloud,调用归 Dubbo,一张能力对照表
- 核心机制:
dubbo.registry.address: spring-cloud://localhost把注册发现委托给 SpringCloud - 分水岭:
spring-cloud://localhost与nacos://127.0.0.1:8848的区别,以及后者为什么会出怪问题 - 整合后的架构图:注册走 Nacos、调用走 Dubbo 两条路径
- 整合前后对比:调用方式 / 序列化 / 注册方式 / 依赖 / 契约 / 性能 / 跨语言
- 工程形状:
cloud-dubbo-demo的模块划分与每个模块的角色 - 落地契约:共享
dubbo-api接口 +@DubboService/@DubboReference的代码落点 - 一个被忽略的治理问题:Feign 与 Dubbo 两套调用方式并存
- 常见误解澄清
为什么要整合:先看两个框架缺什么
先把问题摆正。微服务里"服务 A 调服务 B"这件事,SpringCloud 体系和 Dubbo 体系给了两套完全不同的答案,两套都有短板。
只用 SpringCloud,默认就是 Feign 那一套。 Feign 的调用是声明式 HTTP:你写一个接口,加 @FeignClient,框架帮你拼出 GET /user/{id} 这样的请求,响应用 JSON 传回来。问题在于这条链路层层叠加:
- 协议层:HTTP 是应用层协议,本质上是对 TCP 的再封装,多一层封装就多一层开销;
- 数据层:JSON 是文本,序列化和反序列化都要走字符解析,报文体积也比二进制大;
- 连接层:HTTP 短连接或连接池管理的连接,和 Dubbo 默认基于 Netty 的 TCP 长连接不是一回事。
低并发下这些差异感知不到,但高并发下 HTTP + JSON 的序列化、报文体积和连接管理都会变成瓶颈。
Feign 还有第二个问题,比性能更隐蔽:接口契约容易漂移 。Feign 的接口是消费者自己声明的,@GetMapping 写的路径、参数名、返回类型靠人工和提供者对齐。提供者改了路径或字段,编译期不会报错,跑起来才知道,而且往往是线上才发现。
只用 Dubbo,缺的是微服务生态。 Dubbo 做得最好的那一层是"服务调用与治理"------负载均衡、超时重试、多版本、启动检查这些调用层能力很完整。但一个完整的微服务架构不止调用:网关、配置中心、链路追踪、统一的注册发现方案,这些 Dubbo 本身没有一套完整的方案,或者说它只覆盖了调用这一段。
一句话概括:Feign 有生态但调用效率一般,Dubbo 调用快但生态不全。
整合的目标:能力各归其位
整合的思路不是"二选一",而是让两个框架各自承担它最擅长的那层。
| 能力 | SpringCloud 提供 | Dubbo 提供 | 整合后由谁承担 |
|---|---|---|---|
| 服务注册与发现 | Nacos / Eureka 等注册中心 | 自身的注册中心(ZooKeeper、Nacos 直连) | SpringCloud(Dubbo 委托给它) |
| 配置中心 | Nacos Config | 无统一方案 | SpringCloud |
| 网关 | SpringCloud Gateway | 无 | SpringCloud |
| 熔断限流 | Sentinel / Hystrix | 调用层重试、超时 | SpringCloud 为主,Dubbo 兜调用层 |
| 服务调用协议 | Feign(HTTP + JSON) | Dubbo 协议(TCP + 二进制) | Dubbo |
| 序列化 | JSON | Hessian2 等二进制 | Dubbo |
| 负载均衡 | Ribbon / LoadBalancer | Dubbo 自带 | Dubbo(调用层) |
| 接口契约 | 各自声明 | 共享 api 模块 | Dubbo |
落到工程里就是一句话:注册中心、配置中心、网关、熔断限流用 SpringCloud 那套,服务之间的调用换成 Dubbo。
这也是 SpringCloud Alibaba 做的事------它不是把 Dubbo 塞进 SpringCloud 当外挂,而是让 Dubbo 直接接入 SpringCloud 的注册中心和配置体系,两边共用一套治理基础设施。
整合的核心机制:把注册发现委托给 SpringCloud
整篇最值钱的一行配置,是下面这行:
yaml
dubbo:
registry:
address: spring-cloud://localhost
它的字面意思是"注册中心地址是 spring-cloud://localhost",但这个 localhost 不是一台真实主机 ,它是个固定占位写法。这行的真实语义是:Dubbo 不要自己维护注册信息,把注册和发现全部委托给 SpringCloud 用的那个注册中心 。本工程里 SpringCloud 用的注册中心是 Nacos(spring.cloud.nacos.discovery.server-addr: localhost:8848),所以 Dubbo 的服务列表最终也是从 Nacos 拿的。
这带来一个直接结论:服务的注册名以 spring.application.name 为准,也就是 SpringCloud 体系里的名字,Dubbo 不再自己造一份注册信息。
yaml
spring:
application:
name: user-service # Dubbo 的注册名也以它为准
分水岭:委托,还是自己连
这里有个必须分清的地方,很多整合失败的案例都卡在这。dubbo.registry.address 有两种典型写法:
| 写法 | 含义 | 结果 |
|---|---|---|
spring-cloud://localhost |
Dubbo 把注册发现委托给 SpringCloud 的注册中心 | 一套注册中心、一份服务列表,治理统一 |
nacos://127.0.0.1:8848 |
Dubbo 自己去连 Nacos,走 Dubbo 原生注册 | 两套体系并存,Dubbo 的注册数据与 SpringCloud 的不是同一份视角 |
第二种写法看起来"也能用"------服务确实注册到了同一个 Nacos 实例上,所以很容易误以为等价。但它绕开了 SpringCloud 的注册体系,Dubbo 拿的是 Dubbo 自己写进去的那份注册数据,SpringCloud 的注册数据又是另一份。结果就是常说的那种怪现象:服务明明注册上了,消费者就是发现不到 。或者 Nacos 控制台上能看到实例,但 Dubbo 调用报 No provider available。
所以整合时这一行的写法是硬约束,不要顺手改成 nacos://。判断标准很简单:整合的目标是"一套注册中心、两次使用",不是"两个框架各自连同一个 Nacos"。
整合后的架构
整合完成后,一次外部请求的路径和两条不同的"通道"是这样的------注意实线和虚线的区别,它们不是同一条路:
#mermaid-svg-C7cZWS9K48VcUnfu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-C7cZWS9K48VcUnfu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C7cZWS9K48VcUnfu .error-icon{fill:#552222;}#mermaid-svg-C7cZWS9K48VcUnfu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C7cZWS9K48VcUnfu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C7cZWS9K48VcUnfu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C7cZWS9K48VcUnfu .marker.cross{stroke:#333333;}#mermaid-svg-C7cZWS9K48VcUnfu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C7cZWS9K48VcUnfu p{margin:0;}#mermaid-svg-C7cZWS9K48VcUnfu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-C7cZWS9K48VcUnfu .cluster-label text{fill:#333;}#mermaid-svg-C7cZWS9K48VcUnfu .cluster-label span{color:#333;}#mermaid-svg-C7cZWS9K48VcUnfu .cluster-label span p{background-color:transparent;}#mermaid-svg-C7cZWS9K48VcUnfu .label text,#mermaid-svg-C7cZWS9K48VcUnfu span{fill:#333;color:#333;}#mermaid-svg-C7cZWS9K48VcUnfu .node rect,#mermaid-svg-C7cZWS9K48VcUnfu .node circle,#mermaid-svg-C7cZWS9K48VcUnfu .node ellipse,#mermaid-svg-C7cZWS9K48VcUnfu .node polygon,#mermaid-svg-C7cZWS9K48VcUnfu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C7cZWS9K48VcUnfu .rough-node .label text,#mermaid-svg-C7cZWS9K48VcUnfu .node .label text,#mermaid-svg-C7cZWS9K48VcUnfu .image-shape .label,#mermaid-svg-C7cZWS9K48VcUnfu .icon-shape .label{text-anchor:middle;}#mermaid-svg-C7cZWS9K48VcUnfu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C7cZWS9K48VcUnfu .rough-node .label,#mermaid-svg-C7cZWS9K48VcUnfu .node .label,#mermaid-svg-C7cZWS9K48VcUnfu .image-shape .label,#mermaid-svg-C7cZWS9K48VcUnfu .icon-shape .label{text-align:center;}#mermaid-svg-C7cZWS9K48VcUnfu .node.clickable{cursor:pointer;}#mermaid-svg-C7cZWS9K48VcUnfu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C7cZWS9K48VcUnfu .arrowheadPath{fill:#333333;}#mermaid-svg-C7cZWS9K48VcUnfu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C7cZWS9K48VcUnfu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C7cZWS9K48VcUnfu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C7cZWS9K48VcUnfu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C7cZWS9K48VcUnfu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C7cZWS9K48VcUnfu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C7cZWS9K48VcUnfu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C7cZWS9K48VcUnfu .cluster text{fill:#333;}#mermaid-svg-C7cZWS9K48VcUnfu .cluster span{color:#333;}#mermaid-svg-C7cZWS9K48VcUnfu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-C7cZWS9K48VcUnfu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C7cZWS9K48VcUnfu rect.text{fill:none;stroke-width:0;}#mermaid-svg-C7cZWS9K48VcUnfu .icon-shape,#mermaid-svg-C7cZWS9K48VcUnfu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C7cZWS9K48VcUnfu .icon-shape p,#mermaid-svg-C7cZWS9K48VcUnfu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C7cZWS9K48VcUnfu .icon-shape .label rect,#mermaid-svg-C7cZWS9K48VcUnfu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C7cZWS9K48VcUnfu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C7cZWS9K48VcUnfu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C7cZWS9K48VcUnfu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-C7cZWS9K48VcUnfu .sc>*{fill:#e8f1ff!important;stroke:#3b6ea5!important;color:#1b3a5c!important;}#mermaid-svg-C7cZWS9K48VcUnfu .sc span{fill:#e8f1ff!important;stroke:#3b6ea5!important;color:#1b3a5c!important;}#mermaid-svg-C7cZWS9K48VcUnfu .sc tspan{fill:#1b3a5c!important;}#mermaid-svg-C7cZWS9K48VcUnfu .reg>*{fill:#fff3d6!important;stroke:#b8860b!important;color:#5c4200!important;}#mermaid-svg-C7cZWS9K48VcUnfu .reg span{fill:#fff3d6!important;stroke:#b8860b!important;color:#5c4200!important;}#mermaid-svg-C7cZWS9K48VcUnfu .reg tspan{fill:#5c4200!important;} Dubbo 协议 二进制 RPC
:20881
注册 / 发现(SpringCloud 体系)
注册 / 发现(SpringCloud 体系)
客户端
Gateway
SpringCloud 网关
order-service
SpringCloud 应用 / 消费者
:8082
user-service
SpringCloud 应用 / 提供者
:8081
Nacos
:8848
order 库
user 库
两条路径要分开看:
- 实线(
==>)是注册与发现通道 :order-service和user-service都以 SpringCloud 应用的身份注册到 Nacos,Dubbo 复用这条通道拿服务列表。这条通道归 SpringCloud。 - 虚线(
-.->)是服务调用通道 :order-service调user-service时,走的是 Dubbo 协议(端口 20881)的二进制 RPC,不是 HTTP。这条通道归 Dubbo。
外部请求的入口仍然是 SpringCloud 的网关,网关路由到微服务------这些微服务既是 SpringCloud 应用(注册、配置、网关都沿用),内部又用 Dubbo 互相调用。这就是"SpringCloud Alibaba 体系"整合 Dubbo 之后的样子。
顺带说明一个事实:本篇实读的示例工程 cloud-dubbo-demo 里只有上图中的 order-service 和 user-service 两个业务模块,没有网关模块。网关属于整体架构层面的讨论,工程侧的重点是"注册委托 + Dubbo 调用"这套最小闭环。
整合前后对比
| 维度 | 整合前(只用 SpringCloud / Feign) | 整合后(Dubbo 承接调用) |
|---|---|---|
| 调用方式 | Feign 声明式 HTTP 调用 | Dubbo RPC 调用 |
| 传输协议 | HTTP(应用层,对 TCP 的再封装) | Dubbo 协议 / TCP(默认基于 Netty 长连接) |
| 序列化 | JSON(文本) | Hessian2 等二进制 |
| 注册方式 | SpringCloud 注册中心原生注册 | Dubbo 委托 SpringCloud 注册中心 |
| 消费者依赖 | spring-cloud-starter-openfeign |
dubbo-spring-boot-starter(此处为 spring-cloud-starter-dubbo)+ nacos-discovery |
| 接口契约 | 消费者各自 @FeignClient 声明 |
共享 dubbo-api 模块,同一份接口 |
| 契约一致性 | 靠人工对齐,改错编译期不报错 | 接口在同一模块,契约漂移会被编译拦住 |
| 性能取向 | 通用、够用,高并发下序列化与报文是瓶颈 | 面向高频内部调用,吞吐与延迟更优 |
| 跨语言能力 | 强(HTTP + JSON 天然通用) | 相对弱(Dubbo 生态以 Java 为主,跨语言需额外协议支持) |
| 调试便利性 | 强(curl、浏览器、抓包都能看) | 弱(二进制协议,需专用工具) |
给出一个不偏不倚的判断:Dubbo 在性能和契约一致性上确实更好,Feign 在跨语言和调试便利性上更好。 所以选型看场景,不是一边倒------内部高频调用走 Dubbo,对外开放接口、需要跨语言或需要方便调试的链路,HTTP + JSON 反而更合适。工程里两种调用方式完全可以共存。
工程结构:整合后的模块划分
实读 cloud-dubbo-demo 的父 pom,modules 里是四个模块,包名统一在 cn.mf 下:
tree
cloud-dubbo-demo
├── pom.xml # 父工程:聚合 4 个模块 + 依赖版本管理
├── dubbo-domains # 实体层:User / Order,被 api 与两个服务依赖
│ └── pom.xml
├── dubbo-api # 契约层:UserService 接口,提供者实现、消费者引用
│ └── pom.xml
├── user-service # 提供者:SpringCloud 应用,端口 8081,Dubbo 协议 20881
│ ├── pom.xml
│ └── src/main/java/cn/mf/user
│ ├── UserApplication.java
│ ├── controller/UserController.java
│ ├── mapper/UserMapper.java
│ └── service/impl/UserServiceImpl.java
└── order-service # 消费者:SpringCloud 应用,端口 8082
├── pom.xml
└── src/main/java/cn/mf/order
├── OrderApplication.java
├── controller/OrderController.java
├── mapper/OrderMapper.java
└── service/impl/OrderServiceImpl.java
每个模块在整合方案里的角色:
dubbo-api:契约模块。Dubbo 的接口必须提供者和消费者共用同一份,所以UserService抽出来放这里,两边都依赖它。这是 Dubbo 比 Feign 在契约上更稳的根本原因。dubbo-domains:实体模块。User、Order是跨进程传输的对象,接口签名里用得到,所以单独拆出来给dubbo-api依赖。user-service:提供者。它同时是两个身份------既注册到 Nacos(SpringCloud 应用),又通过@DubboService暴露 Dubbo 服务。order-service:消费者。同样注册到 Nacos,通过@DubboReference引用远端的UserService。
依赖关系是单向的:dubbo-api → dubbo-domains,两个服务各自依赖 dubbo-api。父工程用 dependencyManagement 锁定版本组合,实读到的关键版本是:
| 组件 | 版本 |
|---|---|
| Spring Boot | 2.3.9.RELEASE |
| Spring Cloud | Hoxton.SR10 |
| Spring Cloud Alibaba | 2.2.5.RELEASE |
| MySQL 驱动 | 5.1.47 |
| mybatis-spring-boot-starter | 2.1.1 |
| JDK | 1.8 |
SpringCloud Alibaba 的 spring-cloud-alibaba-dependencies 是整篇整合的版本引子,加在父工程的 dependencyManagement 里,其余两个模块只需要引 spring-cloud-starter-alibaba-nacos-discovery 和 spring-cloud-starter-dubbo,版本由它统一管。
契约怎么落到代码上
整合方案的三个落点,各对应一段代码。契约先落在共享模块里:
java
package cn.mf.dubbo.api;
import cn.mf.dubbo.domain.User;
public interface UserService {
User queryById(Long id);
}
提供者把这个接口实现出来,把 Spring 的 @Service 换成 Dubbo 的 @DubboService,这一换就是"对外暴露成 Dubbo 服务":
java
package cn.mf.user.service.impl;
import cn.mf.dubbo.api.UserService;
import cn.mf.dubbo.domain.User;
import cn.mf.user.mapper.UserMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
@DubboService
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id) {
return userMapper.findById(id);
}
}
消费者用 @DubboReference 注入远端的接口------注意它注入的是 dubbo-api 里的那个接口,本地没有任何实现类,调用时由 Dubbo 代理转发到 user-service:
java
package cn.mf.order.controller;
import cn.mf.dubbo.api.UserService;
import cn.mf.dubbo.domain.Order;
import cn.mf.dubbo.domain.User;
import cn.mf.order.service.OrderService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@DubboReference
private UserService userService;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
Order order = orderService.queryOrderById(orderId);
Long userId = order.getUserId();
User user = userService.queryById(userId);
order.setUser(user);
return order;
}
}
消费者侧的配置除了 registry.address 委托之外,还有两行值得先记住:consumer.check: false 关掉 Dubbo 的启动检查(否则提供者没起来,消费者直接启动失败),retries: 0 关掉内置重试(否则超时后会静默重发,对非幂等接口是事故源)。这两项的取舍在后面的实现篇里展开。
这段"接口抽取 → @DubboService → @DubboReference"的完整落地步骤,属于同系列下一篇的内容,这里只点到为止。
一个容易被忽略的治理问题:两套调用方式并存
整合之后,工程里同时存在 Feign 和 Dubbo 是完全可能的------老的 Feign 调用没删,新的 Dubbo 调用已经加上。技术上没问题,但团队层面有个坑:同一个项目里两种调用风格混用,后来者不知道该照哪种写。
所以整合不是"引个依赖就完事",至少要在团队内约定清楚:
- 什么场景走 Dubbo(服务内部高频调用、契约稳定、团队都是 Java)
- 什么场景保留 Feign(对外接口、跨语言、需要方便调试)
- 新代码默认用哪种,存量代码要不要逐步迁
不定这个规矩,过半年就是同一个仓库里两种风格各自长草,改一个调用得先判断它是哪种。这是很多团队整合 Dubbo 时唯一漏掉的一步。
常见误解澄清
"整合了 Dubbo 就不能用 Feign 了。" 不对。两套调用方式可以共存,spring-cloud-starter-openfeign 和 spring-cloud-starter-dubbo 互不冲突,同一个应用里既能 @FeignClient 也能 @DubboReference。要不要用、用在哪,是治理约定问题,不是框架限制。
"Dubbo 取代了 SpringCloud 的注册中心。" 不对,是委托 。服务的注册和发现仍然发生在 Nacos 上,Dubbo 只是不再自己维护一份注册信息,复用了 SpringCloud 的那套。写 spring-cloud://localhost 的全部意义就在于此。
"spring-cloud://localhost 里的 localhost 是注册中心的主机。" 不是。它是固定占位写法,真实注册中心地址来自 spring.cloud.nacos.discovery.server-addr。把 localhost 改成别的 IP 不会改变任何行为------想指注册中心,得改 SpringCloud 的配置,不是改这一行。
"整合后必须全量改造所有调用。" 不必。整合是可按服务、按接口逐步推进的,新的调用用 Dubbo,旧的 Feign 先留着,跑稳了再迁。全量改造反而容易一次性引入太多变量,出问题难定位。
API 速览
| 配置 / 注解 | 位置 | 作用 |
|---|---|---|
dubbo.registry.address: spring-cloud://localhost |
消费者 + 提供者 | 把注册发现委托给 SpringCloud 注册中心 |
dubbo.protocol.name / port |
提供者 | Dubbo 协议名与端口(示例 20881) |
dubbo.scan.base-packages |
提供者 | 扫描 @DubboService 的包路径 |
dubbo.consumer.check |
消费者 | 启动检查开关,示例关为 false |
dubbo.consumer.retries |
消费者 | 内置重试次数,示例设为 0 |
spring.cloud.nacos.discovery.server-addr |
双方 | 真正的注册中心地址 |
@DubboService |
提供者实现类 | 把实现暴露为 Dubbo 服务 |
@DubboReference |
消费者字段 | 引用远端 Dubbo 服务 |
spring-cloud-alibaba-dependencies |
父 pom | 统一管理 SpringCloud Alibaba 组件版本 |
官方文档
总结
整合的动机来自两边各自的短板:Feign + HTTP 在高并发下序列化和报文开销偏大、契约容易漂移;Dubbo 调用快但缺网关、配置中心这类生态能力。整合的办法是让 SpringCloud 管治理、让 Dubbo 管调用。
技术上最关键的一行是 dubbo.registry.address: spring-cloud://localhost。它让 Dubbo 把注册发现委托给 SpringCloud 的注册中心,从而做到一套注册中心、一份服务列表。反过来写成 nacos://... 就变成两套体系并存,"注册上了但发现不到"的怪问题多半出在这里。
工程侧的形态是 cloud-dubbo-demo 的四模块结构:dubbo-api 定契约、dubbo-domains 放实体、user-service 提供、order-service 消费。模块和配置怎么落地、注解怎么替换,下一篇在同一个工程上逐步实现。