什么是 Dubbo 的优雅停机,怎么实现的?

#中间件#面试八股

什么是 Dubbo 的优雅停机,怎么实现的?

结论先行:优雅停机是“先摘流量、再等在途请求跑完、最后关资源”的有序退出。Dubbo 通过 JVM 关闭钩子(ShutdownHook)触发:注销注册中心地址 → 等待通知传播 → 停止接收新请求 → 等待在途请求结束(默认 10 秒)→ 关闭线程池与连接。用 kill -9 会跳过全部步骤。

一、六步退出流程

SIGTERM / Spring close
        │
        ▼
① 反注册:从注册中心删除本机地址(临时节点会话结束也会删)
        │
        ▼
② 传播等待:给消费端一点时间收到变更通知(通常 1~3 秒)
        │
        ▼
③ 拒绝新请求:协议层标记关闭,新请求直接失败或重定向
        │
        ▼
④ 等待在途:等在处理中的请求完成,超时上限 shutdown.wait
        │
        ▼
⑤ 关线程池、关连接、清缓存
        │
        ▼
⑥ 退出 JVM

二、最容易踩的坑

坑后果处理
用 kill -9 停进程钩子不执行,在途请求全部失败用 kill -15,容器里配 STOPSIGNAL SIGTERM
注册中心通知有延迟地址已删但消费端还在打反注册后 sleep 2~3 秒再停
提供者先关、消费端后关消费端调用报错先停消费端,再停提供端
长耗时任务不受控超时被强杀拆分异步任务,或调大 shutdown.wait
容器滚动更新瞬间切流新老 Pod 同时不可用配 preStop 钩子 + 就绪探针

三、配置与实现

Dubbo 侧:

dubbo:
  service:
    shutdown:
      wait: 10000        # 等待在途请求的毫秒数
  provider:
    delay: 0             # 延迟暴露,避免未就绪就接流

Spring Boot 侧要保证钩子先于连接池关闭执行:

server:
  shutdown: graceful      # Web 容器也走优雅停机
spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s

Kubernetes 侧:

lifecycle:
  preStop:
    exec:
      command: ["sh", "-c", "sleep 5"]
terminationGracePeriodSeconds: 30

sleep 5 给 Service 摘除 Endpoint 与注册中心通知留出时间,terminationGracePeriodSeconds 必须大于 preStop 加 shutdown.wait,否则会被 SIGKILL。

四、验证是否真的优雅

压测中执行 kill -15,观察三件事:
1. 注册中心里该实例是否在 1~3 秒内消失
2. 消费端日志是否只有“地址变更”,没有 RpcException
3. 提供端日志里在途请求是否都打印了完成

常见追问

  • 追问:为什么不能只依赖注册中心的临时节点删除?会话超时依赖心跳,最长可能几十秒,期间流量仍在打向已死实例,所以必须主动反注册。
  • 追问:shutdown.wait 设多大?取 P99 耗时加余量,通常 5~10 秒;设太大拖慢发布,设太小会截断长请求。
  • 追问:消费端需要优雅停机吗?需要。消费端要等在途响应回来、把未发送的请求处理完,否则同样会产生超时错误。

小结

  • 顺序是“摘流量 → 等传播 → 拒新请求 → 等在途 → 关资源”。
  • 必须用 SIGTERM,容器要配 preStop 和足够的宽限期。
  • 发布顺序:先停消费端,再停提供端。
  • 优雅停机的效果要用压测验证,不能只看配置。
笔记加载中…