Java 项目实战: 交友项目 - SpringCloud 整合 Dubbo 的方案概述

概述

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 消费。模块和配置怎么落地、注解怎么替换,下一篇在同一个工程上逐步实现。

相关推荐
imDwAaY1 小时前
程序里的地址是真实内存地址吗?从虚拟内存到地址转换
java·linux·服务器
秋饼2 小时前
Claude Haiku 5.5 + Spring AI 2.0 企业级低成本高并发 AI Agent 实战:子智能体分工、成本熔断与百万上下文落地
java·ai·技术分享·后端开发
xuxigifxfh2 小时前
牛客:BISHI8 大整数哈希
java·算法·哈希算法
涉密IT资质笔记2 小时前
涉密资质投入产出分析模型:时间成本、四块成本构成与 ROI 决策框架
java·开发语言·算法
木井巳2 小时前
【BFS 解决拓扑排序】火星词典
java·算法·leetcode·广度优先·宽度优先·图搜索算法
霸道流氓气质2 小时前
Java调用StableDiffusion/ComfyUI图像生成:DAG工作流编排、WebSocket实时进度与Flux模型集成的生产级实战
java·websocket·stable diffusion
北风toto2 小时前
KMP 算法详解:从暴力匹配到 O(n+m) 的字符串搜索
java·开发语言·算法
weixin199701080162 小时前
《京东JOS API:落地方案、接口边界与业务踩坑 —— 联盟免费 vs 商家按量的双轨制》(附python源码)
java·网络·python
衡石科技2 小时前
从语义准备到模型验收:HENGSHI SENSE Data Agent 的 RAG 与部署工程实践
java·大数据·人工智能·数据分析·rag