Skip to content

Dubbo 专栏

Dubbo 是 Java 生态里非常典型的高性能 RPC 框架。它解决的核心问题不是“怎么发 HTTP 请求”,而是:服务拆成多个进程以后,调用方怎样像调用本地接口一样调用远程服务,同时还能做到注册发现、负载均衡、超时、重试、容错、治理、监控和扩展。

学习 Dubbo 不能只背 @DubboService@DubboReference。真正要理解的是:

  1. 为什么微服务需要 RPC。
  2. 一次 Dubbo 调用从代理对象到网络传输再到服务端反射执行经历了什么。
  3. 注册中心在 Dubbo 里保存什么,不负责什么。
  4. 协议、序列化、连接、线程池、负载均衡、集群容错分别解决什么问题。
  5. Dubbo SPI 为什么是核心扩展机制。
  6. 商业项目中哪些场景适合 Dubbo,哪些场景更适合 HTTP、MQ 或事件驱动。

学习目标

学完本专栏后,你应该能做到:

能力具体要求
会用能写 provider、consumer、接口包和注册中心配置
懂链路能画出一次远程调用的完整流程
懂原理能解释代理、Invoker、Directory、Router、LoadBalance、Cluster、Protocol、Codec、Transport
会治理能配置超时、重试、负载均衡、灰度、版本、分组、降级
会排查能定位注册失败、找不到服务、超时、线程池打满、序列化失败、重复调用
会面试能用标准回答讲清 Dubbo 和 Spring Cloud OpenFeign 的区别

本专栏学习路径

阶段文档学习重点
入门RPC 与 Dubbo 总览先理解 Dubbo 解决什么问题
核心调用链路与核心架构掌握代理、Invoker、Filter、Protocol、Exchange、Transport
注册注册发现、Directory刷新与故障恢复理解接口级/应用级发现、注册订阅、元数据、地址对账和Session窗口
协议协议、序列化与线程模型理解 Dubbo 协议、Hessian2、连接复用、线程池
治理路由、负载均衡、Cluster与重试预算理解逻辑调用与物理尝试、算法、超时预算、灰度和Provider保护
扩展Dubbo SPI 与扩展点理解 Dubbo 为什么能高度可插拔
面试Dubbo 面试题标准回答、追问点、项目话术和跳转

为什么需要 RPC

单体应用里,订单模块调用库存模块通常就是一个普通方法:

java
stockService.deduct(skuId, count);

拆成微服务后,订单服务和库存服务可能在不同机器、不同 JVM 进程中。此时本地方法调用不再成立,必须通过网络调用:

mermaid
flowchart TD
    A["订单服务进程"] --> B["网络"]
    B --> C["库存服务进程"]
    C --> D["库存数据库"]

如果每个服务都手写 Socket、序列化、超时、连接池、负载均衡、重试、服务发现,业务代码会非常混乱。RPC 框架的价值,就是把这些通用能力封装起来,让业务开发面向接口编程。

RPC 和 HTTP 调用有什么区别

RPC 不是一定比 HTTP 高级,它们解决的问题有重叠,也有差异。

对比项Dubbo RPCSpring Cloud OpenFeign HTTP
调用体验像本地 Java 接口调用像声明式 HTTP 客户端
协议Dubbo 协议、Triple、REST 等HTTP/1.1、HTTP/2
序列化Hessian2、Fastjson2、Protobuf 等JSON 最常见
性能二进制协议通常更轻JSON 可读性强但体积较大
契约Java 接口契约强HTTP API 契约更开放
跨语言传统 Dubbo Java 生态强,Triple 更利于跨语言跨语言天然友好
治理服务治理能力非常丰富依赖 Spring Cloud 组件组合
适合场景Java 内部服务、高性能强契约调用开放 API、前后端、跨语言、云原生 HTTP 主线

商业项目里常见组合是:外部入口用 HTTP/Gateway,内部核心 Java 服务之间用 Dubbo;跨系统异步解耦用 MQ。

一次 Dubbo 调用的简化流程

mermaid
flowchart TD
    A["Consumer 注入接口代理"] --> B["调用接口方法"]
    B --> C["代理转换成 Invocation"]
    C --> D["从注册中心获取 Provider 地址"]
    D --> E["路由过滤不可用实例"]
    E --> F["负载均衡选择一个实例"]
    F --> G["编码和序列化请求"]
    G --> H["Netty 发送网络请求"]
    H --> I["Provider 解码请求"]
    I --> J["Filter 链处理"]
    J --> K["反射调用真实服务方法"]
    K --> L["序列化响应返回 Consumer"]

这张图说明两件事:

  1. Dubbo 的“像本地调用”只是编程体验,底层仍然是网络调用。
  2. 网络调用一定会出现超时、失败、重复、半成功,所以业务仍然要设计幂等、超时、降级和补偿。

最小 Demo:订单服务调用库存服务

项目通常拆成三个模块:

模块作用
stock-api放公共接口和 DTO,provider 与 consumer 都依赖
stock-provider实现库存服务并暴露 Dubbo 服务
order-consumer引用库存服务并发起远程调用

公共接口

java
package com.example.stock.api;

public interface StockService {
    boolean deduct(String skuId, int count);
}

Provider 实现

java
package com.example.stock.provider;

import com.example.stock.api.StockService;
import org.apache.dubbo.config.annotation.DubboService;

@DubboService(version = "1.0.0", group = "trade")
public class StockServiceImpl implements StockService {

    @Override
    public boolean deduct(String skuId, int count) {
        if (count <= 0) {
            throw new IllegalArgumentException("count must be positive");
        }
        System.out.println("扣减库存 skuId=" + skuId + ", count=" + count);
        return true;
    }
}

Provider 配置

yaml
server:
  port: 8081

dubbo:
  application:
    name: stock-provider
  protocol:
    name: dubbo
    port: 20880
  registry:
    address: zookeeper://127.0.0.1:2181
  scan:
    base-packages: com.example.stock.provider

Consumer 引用

java
package com.example.order;

import com.example.stock.api.StockService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @DubboReference(
            version = "1.0.0",
            group = "trade",
            timeout = 2000,
            retries = 0,
            loadbalance = "roundrobin"
    )
    private StockService stockService;

    @PostMapping("/orders")
    public String createOrder(@RequestParam String skuId, @RequestParam int count) {
        boolean success = stockService.deduct(skuId, count);
        return success ? "order created" : "stock not enough";
    }
}

Consumer 配置

yaml
server:
  port: 8082

dubbo:
  application:
    name: order-consumer
  registry:
    address: zookeeper://127.0.0.1:2181

这个 demo 里最重要的不是注解,而是配置背后的含义:

配置含义不配置或配错会怎样
registry.address注册中心地址provider 注册不上,consumer 找不到服务
protocol.portprovider 暴露 RPC 端口端口冲突或防火墙不通会调用失败
version服务版本多版本共存时避免调用错实现
group服务分组多业务线或多环境隔离
timeout远程调用超时不设置可能长时间阻塞调用线程
retries失败重试次数写接口重试可能造成重复扣减

商业常用场景

场景Dubbo 适合点必须注意
订单调用库存Java 内部服务强契约调用扣库存接口要幂等,写操作慎用重试
资产平台调用字典服务低延迟、接口稳定字典可缓存,避免每次远程调用
医疗采集服务调用规则服务接口治理、版本控制规则版本要隔离,灰度发布
权限服务被多个系统调用公共能力复用权限结果缓存和降级策略
批处理调用基础服务高吞吐内部调用控制并发,避免打满 provider 线程池

初学者必须建立的正确认知

误区为什么不对正确理解
Dubbo 调用就是本地调用底层经过网络,可能失败和超时编程像本地,运行时是远程
有注册中心就不会调用失败注册中心只管地址发现调用还受网络、线程池、序列化、超时影响
重试越多越可靠写接口重复执行会造成脏数据查询可重试,写接口优先幂等和补偿
Dubbo 一定比 HTTP 好场景不同内部 Java 强契约适合 Dubbo,开放和跨语言适合 HTTP
Provider 机器多就一定稳定慢实例也会拖垮调用方需要超时、熔断、限流、隔离和监控

和其他知识点的关系

面试标准回答

Dubbo 是 Java 生态常用的高性能 RPC 框架。它通过接口代理屏蔽远程调用细节,Consumer 调用接口时会把方法、参数、附件等封装成 Invocation,再经过集群容错、路由、负载均衡选择 Provider,之后通过协议编码、序列化和网络通信发送到服务端。Provider 解码后经过 Filter 链,最终调用真实服务实现并返回结果。Dubbo 还提供注册发现、版本分组、超时重试、负载均衡、熔断降级、SPI 扩展等服务治理能力。

追问时要补一句:Dubbo 的本地调用体验不代表它是本地调用,底层仍然是不可靠网络,所以生产上必须设置超时、控制重试、设计幂等、做监控告警和降级。

本章小结

Dubbo 的核心不是几个注解,而是一套完整的 RPC 调用和服务治理体系。学 Dubbo 要从远程调用的不可靠性出发,理解代理、注册发现、路由、负载均衡、协议、序列化、线程模型、容错和扩展点。只有这样,线上遇到“找不到服务、调用超时、重复扣减、线程池打满、序列化失败”时,才知道该从哪条链路排查。