Dubbo 的优雅停机 (Graceful Shutdown)是指在服务关闭时,系统能够平滑处理未完成请求 、拒绝新请求 ,并有序释放资源 ,避免直接停机导致的请求中断或数据不一致问题。其核心目标是最小化停机对业务的影响,在高并发场景下一定要处理的。
优雅停机触发时机
Dubbo 的优雅停机通常在以下场景触发:
- 服务主动下线(如运维手动重启、版本发布)。
- JVM 关闭 (通过
kill -15
或System.exit()
发送 SIGTERM 信号)。 - 容器销毁(如 Kubernetes Pod 终止)。
实现原理与流程
Dubbo 的优雅停机通过 ShutdownHook 机制实现,整体流程如下:
1. 拒绝新请求
-
服务提供者 :
立即向注册中心(如 ZooKeeper、Nacos)注销服务 ,通知消费者停止向其发送新请求。
关闭 Netty 端口监听,拒绝新连接。
-
服务消费者 :
停止从负载均衡列表中获取该提供者节点,不再向其发起新调用。
2. 等待处理中的请求完成
-
提供者侧 :
检查所有正在处理的请求(如线程池中的任务),等待其自然完成 (默认等待 10 秒,可配置)。
若超时仍有未完成请求,记录日志并强制终止(防止无限等待)。
-
消费者侧 :
对于已发出的请求,等待响应或超时;若提供者提前关闭连接,触发失败重试(需配置重试策略)。
3. 释放资源
- 关闭线程池、网络连接(如 Netty Channel)、注册中心客户端等资源。
- 清理内存中的临时数据(如未完成的异步调用上下文)。
核心配置参数
Dubbo 通过以下参数控制优雅停机行为(通常配置在 dubbo.properties
或 Spring 配置中):
参数 | 默认值 | 说明 |
---|---|---|
dubbo.service.shutdown.wait |
10000 |
等待处理中请求完成的最长时间(毫秒) |
dubbo.protocol.destroy.timeout |
10000 |
协议层资源释放超时时间 |
dubbo.registry.delay |
0 |
注册中心通知延迟时间(毫秒),设为 0 表示立即注销 |
实现细节与源码分析
1. ShutdownHook 注册
Dubbo 在启动时通过 AbstractConfig#start()
注册 JVM 钩子:
java
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
ProtocolConfig.destroyAll(); // 销毁所有协议
RegistryFactory.destroyAll(); // 关闭注册中心连接
}));
2. 服务注销与资源释放
-
服务提供者注销 :
RegistryProtocol
调用unexport()
方法,从注册中心移除服务 URL。 -
消费者断开连接 :
DubboInvoker
调用destroy()
关闭与提供者的长连接。
3. 等待请求完成
通过 ExecutorRepository
监控线程池状态:
java
ExecutorService executor = executorRepository.getExecutor(url);
executor.shutdown(); // 停止接收新任务
executor.awaitTermination(timeout, TimeUnit.MILLISECONDS); // 等待任务完成
验证优雅停机是否生效
1. 日志观察
检查 Dubbo 日志中是否包含以下关键步骤:
log
[INFO] DubboShutdownHook begin to shutdown...
[INFO] Unregister: dubbo://192.168.1.100:20880/com.example.UserService
[INFO] Close Netty Server bind:0.0.0.0/0.0.0.0:20880
[INFO] DubboShutdownHook shutdown completed.
2. 模拟测试
- 步骤 1:启动服务提供者和消费者,发起持续请求(如压测工具)。
- 步骤 2 :向提供者进程发送
kill -15 PID
命令。 - 步骤 3 :观察是否所有进行中的请求均正常返回,且无
Connection refused
错误。
注意事项与最佳实践
-
合理设置超时时间
根据业务最大耗时调整
dubbo.service.shutdown.wait
,避免过早强制终止。 -
避免长时间阻塞操作
若业务逻辑包含同步阻塞调用(如数据库长事务),需优化代码或拆分任务。
-
结合容器生命周期
在 Kubernetes 中,需配置
preStop
Hook 提前触发优雅停机:yamllifecycle: preStop: exec: command: ["sleep", "30"] # 留出时间供Dubbo完成停机
-
兼容异步调用
若使用异步请求(如
CompletableFuture
),需确保异步任务在停机前完成。
与非优雅停机的对比
场景 | 优雅停机 | 强制停机(kill -9) |
---|---|---|
新请求处理 | 立即拒绝 | 可能部分请求被接受但未处理 |
进行中请求 | 等待完成或超时 | 直接中断,导致业务异常 |
资源释放 | 有序释放(连接、线程池) | 资源泄漏风险 |
注册中心状态 | 先注销再停机 | 节点状态滞后,消费者可能调用失败 |
总结
Dubbo 的优雅停机通过注册中心协作 、资源状态监控 和超时控制,实现了服务下线的平滑过渡。合理配置停机参数、优化业务逻辑响应时间,并结合容器化部署策略,可最大限度减少服务重启对业务的影响。