Dubbo 生产部署

📖 前置阅读:本文是 Dubbo 系列的终篇,假设读者已经掌握前五篇的全部内容(核心架构、SpringBoot 集成、集群容错与负载均衡、注册中心、Dubbo 3.x 新特性)。

一、⚡ 问题切入:单机开发的配置能上生产吗?

前五篇的页面配置——超时 1 秒、重试 2 次、单注册中心、无限流无监控——只能用来学习。生产环境:

单点/隐患后果
一个 Nacos 实例Nacos 挂了 → 新 Consumer 启动不了 → 新 Provider 无法注册
没设并发限制一个 Provider 被大量请求打爆 → 线程池满 → 所有请求排队或失败
超时设太短Provider 还在处理,Consumer 已断开 → 重复调用
没有监控Provider 变慢了、Success Rate 下降了——完全不知道
无优雅上下线重启 Provider → 正在处理的请求全部失败

生产最低配:Nacos 集群(至少 3 台) + Provider 并发限制 + Consumer 合理超时 + Dubbo Admin 监控 + 优雅上下线。

二、高可用架构

2.1 Nacos 集群部署

# docker-compose-nacos-cluster.yml
version: '3.8'
services:
  nacos1:
    image: nacos/nacos-server:v2.3.0
    container_name: nacos1
    environment:
      - MODE=cluster
      - NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848
      - NACOS_APPLICATION_PORT=8848
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=mysql
      - MYSQL_SERVICE_DB_NAME=nacos
      - MYSQL_SERVICE_USER=nacos
      - MYSQL_SERVICE_PASSWORD=nacos123
    ports:
      - "8848:8848"
      - "9848:9848"

  # nacos2、nacos3 类似——改端口映射

  mysql:
    image: mysql:8.0
    container_name: nacos-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root123
      - MYSQL_DATABASE=nacos
      - MYSQL_USER=nacos
      - MYSQL_PASSWORD=nacos123
    volumes:
      - ./mysql/data:/var/lib/mysql

Nacos 集群需要 MySQL(生产不能用内置 Derby 数据库——数据不共享)。

2.2 Dubbo 的多注册中心高可用

dubbo:
  registries:
    primary:
      address: nacos://nacos1:8848,nacos2:8848,nacos3:8848  # 集群地址
      default: true

同一个注册中心的多个节点用逗号分隔——Consumer 连接任意一台可用即可。

2.3 Provider 多实例部署

order-provider(3 个实例):
  Instance-1: 192.168.1.10:20880  (weight=200, 4C8G)
  Instance-2: 192.168.1.11:20880  (weight=200, 4C8G)
  Instance-3: 192.168.1.12:20880  (weight=100, 2C4G)  ← 性能差的机器权重低

Consumer 负载均衡:
  40% → Instance-1
  40% → Instance-2
  20% → Instance-3

三、调优

3.1 Provider 端调优

dubbo:
  provider:
    # ===== 线程模型 =====
    threads: 200               # 业务线程池大小(默认 200)
    threadpool: fixed          # fixed / cached / limited / eager
    queues: 0                  # 等待队列大小——0 表示队列满后直接拒绝(有界队列)
    
    # ===== 并发控制 =====
    actives: 500               # 最大并发调用数——超过则等待或拒绝
    executes: 1000             # 最大并发执行数
    
    # ===== 超时 =====
    timeout: 3000              # Provider 端超时(ms)——Consumer 端可覆盖
    
    # ===== 连接控制 =====
    accepts: 500               # 最大连接数
    payload: 8388608           # 最大请求体大小(8MB)

Provider 线程模型

线程池Provider 中的角色默认值
Boss 线程接收 TCP 连接1(Netty 默认)
I/O Worker 线程处理网络 I/O——序列化/反序列化CPU 核数 + 1
业务线程池执行 Provider 的业务逻辑(你的代码)200
Consumer 请求到达 Provider:
  Netty I/O Worker → 反序列化 → 提交到业务线程池 → 执行业务方法 → 返回
                   ↑                                              ↓
              非阻塞——I/O 线程立即                   业务线程执行结束后通知 I/O 线程发响应
              回去接收下一个请求
# 调整线程数
dubbo:
  provider:
    threads: 400            # 业务线程——CPU 密集型:CPU 核数 × 2
                            # I/O 密集型:CPU 核数 × 10~20
    iothreads: 8            # I/O Worker 线程——不要超过 CPU 核数 × 2
参数调大调小依据
threadsRPC 调下游服务多(I/O 等待)纯计算逻辑(CPU 密集)线程数 = CPU 核数 × (1 + 等待时间/计算时间)
iothreads请求量大、序列化开销大CPU 核数少不超过 CPU 核数 × 2——多了上下文切换
activesProvider 处理能力强保护 Provider 不被打爆压测得到的最大并发数 × 0.8
timeoutProvider 处理慢Provider 处理快99 分位延迟 × 1.5

3.2 Consumer 端调优

dubbo:
  consumer:
    timeout: 3000              # 调用超时(ms)
    retries: 0                 # 重试次数——非幂等写操作必须为 0
    loadbalance: p2c           # 负载均衡——Dubbo 3.2+ 推荐
    check: true                # 启动时检查 Provider 是否可用
    connections: 1             # 每个 Provider 的连接数——默认 1(共享连接)

timeout 的设置参考

调用类型timeout 建议原因
简单查询(单表、有索引)1000ms快——超了就重试
复杂查询(多表关联、大数据量)5000ms慢——给足时间
写操作(创建、更新)3000ms中等——但 retries=0
第三方 API 调用(短信、支付)10000ms不可控——给足时间
// 为不同方法设置不同的超时——细粒度控制
@DubboReference(
    timeout = 3000,
    retries = 0,                    // 写操作不重试
    parameters = {
        "getOrderById.timeout", "1000",    // 查询超时 1s
        "createOrder.timeout", "5000"      // 创建超时 5s
    }
)
private OrderService orderService;

3.3 JVM 调优

# Provider JVM 参数
java -jar order-provider.jar \
  -Xms2g -Xmx2g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=50 \
  -XX:InitiatingHeapOccupancyPercent=40 \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/var/log/dubbo/heapdump
参数含义建议值
-Xms2g -Xmx2g堆内存至少 2G——Dubbo 用堆存请求和响应对象
-XX:+UseG1GCG1 垃圾回收器——低延迟必选
-XX:MaxGCPauseMillis=50目标 GC 停顿 < 50ms更小的值会导致更频繁的 GC
-XX:InitiatingHeapOccupancyPercent=40堆使用 40% 开始并发标记默认 45——给 GC 更多提前量

3.4 Netty 层调优

dubbo:
  provider:
    # Netty I/O 调优(通过 -D 参数传递)
    # -Ddubbo.protocol.payload=8388608   # 8MB 请求上限
    # -Ddubbo.protocol.buffer=16384      # 网络缓冲区大小
    # -Ddubbo.protocol.serialization=hessian2

四、Dubbo Admin —— 可视化监控

4.1 核心页面

访问 http://localhost:8081
Tab看什么为什么要看
服务列表所有已注册的 Provider 和 Consumer、接口列表、实例数确认服务是否都在线
服务关系谁调了谁——调用拓扑图找出不合理的依赖(A 调了不该调的 C)
流量管理动态路由规则、权重调整、条件路由无需重启调整流量分配
配置管理Provider/Consumer 的运行时参数调整超时、重试、负载均衡——实时生效
监控调用次数、平均耗时、成功率发现慢调用和异常

4.2 必须盯住的三个指标

指标Dubbo Admin 看哪里告警阈值
Success Rate监控 → 成功率< 99.9%
平均耗时监控 → 响应时间P99 持续增长
Provider 在线数服务列表 → 实例数少于预期实例数
并发调用数服务详情 → 活跃数接近 actives 限制

4.3 动态配置——不重启改参数

Dubbo Admin 支持动态下发配置——修改后实时生效:

在 Dubbo Admin → 配置管理 → 新增配置:

# 针对特定服务的配置覆盖
configVersion: v1.0
enabled: true
configs:
  - side: provider
    key: org.example.api.OrderService
    parameters:
      timeout: 5000          # 调大超时
      actives: 300           # 调整并发限制
      loadbalance: leastactive

动态配置 vs yml 配置的优先级:动态配置 > yml 配置。如果在 Dubbo Admin 中改了 timeout: 5000,会覆盖 yml 中的值。

五、优雅上下线

5.1 优雅下线 —— 重启时不丢请求

# Provider 配置优雅下线
dubbo:
  provider:
    # 服务关闭时等待请求处理完的时间(ms)
    shutdown-timeout: 10000   # 10s 内处理完所有已接收的请求再关闭

优雅下线流程:

1. 收到关闭信号(kill PID / K8s SIGTERM)
2. Provider 从 Registry 注销服务——Consumer 不再收到这个实例的地址
3. 等待 10s——处理完已接收但未完成的请求
4. 10s 到了——强制关闭
# K8s 中配合 preStop hook
# 先注销、再等、再关进程
spec:
  containers:
    - name: order-provider
      lifecycle:
        preStop:
          exec:
            command:
              - /bin/sh
              - -c
              - |
                # 1. 调用 Dubbo 的离线命令——从注册中心注销
                curl -X POST http://localhost:22222/offline
                # 2. 等待 10s——处理完所有正在进行的请求
                sleep 10

5.2 优雅上线 —— 预热

dubbo:
  provider:
    warmup: 120000            # 启动后 120s 内权重从 0 慢慢增加到正常值

新启动的 Provider——JIT 还没编译、缓存还是冷的——性能比老实例差。开启预热后,Dubbo 在预热期内给新实例分配较少流量:

启动第 0s:  weight = 0     → 没流量
启动第 30s: weight = 25%   → 25% 流量
启动第 60s: weight = 50%   → 50% 流量
启动第 90s: weight = 75%   → 75% 流量
启动第 120s: weight = 100  → 100% 流量

六、常见生产故障

故障现象排查
线程池满Provider 日志 RejectedExecutionExceptionthreads 是否设太小 ② Provider 处理逻辑是否有慢调用 ③ 增加实例或调大线程池
Consumer 超时雪崩一个 Provider 变慢 → Consumer 线程全部阻塞等待 → 整个 Consumer 不可用① 设合理的 timeout ② 用 CompletableFuture 异步调用——不阻塞 Consumer 主线程
序列化不兼容Hessian2Exception: expected string but got intProvider 和 Consumer 的 API 版本不一致——字段类型变了。确保 API 模块版本一致
Provider 全部离线Consumer 报 No provider available——Registry 列表为空① Nacos 是否正常 ② Provider 是否因 OOM/GC 停顿导致心跳丢失 ③ 检查 Nacos 的网络连接
内存泄漏Provider Full GC 越来越频繁,最终 OOM① 检查是否在 Provider 方法中把请求对象存到了 static 集合 ② 检查 actives 是否设太大
rebalance 风暴Dubbo 3.x 元数据刷新过于频繁调大 dubbo.metadata-report.retry-timesdubbo.metadata-report.cycle-report

七、上线前 10 项检查清单

#检查项配置/命令
1Nacos 集群部署 ≥ 3 台docker-compose-nacos-cluster.yml 中 3 个 nacos 实例 + MySQL
2Consumer 写操作关重试@DubboReference(retries = 0)cluster = "failfast"
3Provider 设并发限制dubbo.provider.actives——压测最大并发 × 0.8
4Provider 设线程池dubbo.provider.threads——根据 I/O 密集度调整
5Consumer 超时合理查询 1000ms、写操作 3000ms、外部 API 10000ms
6优雅下线dubbo.provider.shutdown-timeout=10000 + K8s preStop hook
7预热开启dubbo.provider.warmup=120000——2 分钟预热
8Dubbo Admin 部署先最小化部署——至少盯住服务列表和成功率
9Provider JVM 堆 ≥ 2G + G1GC-Xms2g -Xmx2g -XX:+UseG1GC
10check=true(默认)Provider 没启动时 Consumer 启动就报错——不要改成 false 掩盖问题

八、Dubbo vs gRPC vs Spring Cloud 最终选型

六篇 Dubbo 学完了。加上之前的三个 MQ 系列——现在选型时:

场景选谁理由
Java 微服务内部 RPC——高吞吐低延迟Dubbodubbo/triple 协议 + 内置服务治理——性能和服务治理一把抓
需要消息重放、流处理Kafka分布式提交日志——核心就是持久化和重放
需要事务消息、延迟消息RocketMQ半消息 + 18 级延迟 + 原生事务——MQ 中事务支持最好
路由灵活、小团队RabbitMQExchange + Binding 灵活度最高,单 Docker 即可
跨语言 RPC——Go/Node.js/Python 调 JavagRPCProtobuf + 多语言 SDK——跨语言是核心优势
对外 API Gateway + 内部调用Dubbo 3 TripleTriple 协议 HTTP/2 + JSON——浏览器可调,内部 Protobuf 性能高
全套 Spring 生态Spring Cloud全家桶——Gateway、Config、Sleuth 全集成
云原生 / Istio / K8sDubbo 3.x Mesh 模式服务治理下沉到 Sidecar——Dubbo 只做 RPC

🎯 总结

Dubbo 的生产部署核心在三点:

  1. 高可用架构:Nacos 集群 ≥ 3 台(数据存在 MySQL),Provider 多实例 + 权重调节。Registry 本地缓存兜底——Nacos 全部宕机也不影响已有连接的调用。

  2. 调优关键是并发和超时:Provider actives 保护自己不被打爆,Consumer timeout 防止雪崩。线程数根据 I/O 密集度调整——threads = CPU 核数 × (1 + 等待时间/计算时间)

  3. 监控盯着三个指标:Dubbo Admin 的 Success Rate、平均耗时、Provider 在线数。支持动态配置——不重启改超时和负载均衡策略。


📖 系列总览

Dubbo 六篇系列到此结束:

#核心收获
1核心架构与 RPC 模型RPC 本质、Dubbo 三角架构、与 REST 的差异、Registry 是"黄页"不是"中转站"
2SpringBoot 全操作指南@DubboService / @DubboReference 两个注解替代全部 XML、dubbo/triple 协议配置、序列化选择
3集群容错与负载均衡六种容错策略、七种负载均衡、异步调用 CompletableFuture、版本分组灰度发布
4注册中心:Nacos 与 Zookeeper服务发现全链路、Nacos 同时做注册中心和配置中心、多注册中心双活
5Dubbo 3.x 新特性Triple 协议(HTTP/2 + Protobuf)、应用级服务发现、curl 直接调 RPC
6生产环境部署与调优Nacos 集群、Provider/Consumer/JVM 三层调优、Dubbo Admin 监控、10 项检查清单

建议从 1 到 6 顺序阅读,每篇以前一篇为前提。学完这六篇,从 RPC 概念到生产部署的全链路都覆盖了。

四个系列的完整技术栈:

  • 消息队列:RabbitMQ(灵活路由)+ RocketMQ(事务消息)+ Kafka(流处理与重放)
  • RPC 框架:Dubbo(Java 微服务内部通信 + 服务治理)