Dubbo服务重启时注册中心下线不及时问题排查与修复记录
Hi,我是阿昌,今天记录一个服务重启时反复出现的 JDBC 报错问题;
排查到最后发现根因是 Dubbo 2.6.2 的注册中心下线机制有坑。
一、问题现象
服务发布重启时,短时间内出现多次 JDBC 连接报错。

看日志发现在容器已经销毁后,注册中心里该服务的节点还在,导致仍有请求被路由到这个已经挂掉的实例上。
简单说就是:
服务实例已经没了,但注册中心以为它还在。
二、根因分析
直接原因很清楚:注册中心下线不及时。
继续往下追,就得看 Dubbo 是怎么做下线的。项目用的是 Dubbo 2.6.2。
三、Dubbo 2.6.2 的下线方式
翻源码发现,2.6.2 是在类加载时注册了一个 JVM shutdown 钩子:
java
// Dubbo 2.6.2 的下线入口
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
ProtocolConfig.destroyAll();
}));
真正执行下线的是 ProtocolConfig.destroyAll()。

这里有两个问题:
问题一:kill -9 时钩子不执行。 这是最主要的原因。发布系统销毁容器时如果走了强制 kill,JVM 的 shutdown hook 根本不会触发,注册中心就不会收到下线通知。
问题二:多线程保证不了执行顺序。 即使钩子顺利执行,shutdown hook 的线程调度顺序也不可控,可能出现资源先于 Dubbo 协议被销毁的情况。
四、Dubbo 2.6.3 的改进
2.6.3 版本对下线机制做了明显的优化:
- 新增
DubboShutdownHook类,把主要的下线逻辑收拢进去 - 2.6.2 原有的下线方法被移除,只保留了兼容调用
- 新增
DubboApplicationListener,通过监听 Spring 容器的ContextClosedEvent来触发下线
这意味着在 Spring 容器正常关闭时(比如 kill -15),Listener 能拿到关闭事件主动去注销服务,比只靠 JVM shutdown hook 靠谱得多。



五、解决方案
两个方向:
方案一:升级 Dubbo 版本到 2.6.3 以上。 直接用官方的改进,最省事。
方案二:仿照 2.6.3 自己加一个 Listener。 不升级版本的情况下,手动补上这个能力。
我当时的做法是方案二,直接写了一个 ShutdownHookListener:
java
@Configuration
public class ShutdownHookListener {
private static Logger log = LoggerFactory.getLogger(ShutdownHookListener.class);
@Bean
DubboShutdownListener dubboShutdownListener() {
return new DubboShutdownListener();
}
public static class DubboShutdownListener implements ApplicationListener, PriorityOrdered {
@Override
public void onApplicationEvent(ApplicationEvent event) {
log.info("销毁开始");
if (event instanceof ContextClosedEvent) {
log.info("开始注销注册中心");
ProtocolConfig.destroyAll();
log.info("服务下线完成");
}
}
@Override
public int getOrder() {
return 0;
}
}
}
思路很简单:
监听 ContextClosedEvent,容器关闭时主动调用 ProtocolConfig.destroyAll() 把服务从注册中心摘掉。
getOrder() 返回 0 保证这个 Listener 在关闭链中尽量早执行,先下线再销毁其他资源。
六、这个方案的一个隐患
上面用了 ProtocolConfig.destroyAll(),这个方法在 Dubbo 2.6.3 源码里有这样一段注释:
"Just for compatibility" ------ 仅仅为了兼容
"It should be deleted in the next major version, say 2.7.x." ------ 在 2.7.x 主要版本中会被删除
也就是说,如果以后升级到 Dubbo 3,这个方法就没了,编译直接报错。
所以在方案二的代码里,后续升级 Dubbo 时需要记得换成高版本的对应下线 API,或者干脆切到方案一直接升版本。

七、总结
这个问题本质上是:
JVM shutdown hook 不可靠 + 发布系统的容器销毁方式 = 注册中心感知不到服务下线。
短期方案是自己加个 Spring Listener 兜底,长期方案是升级 Dubbo 版本。
写代码的时候顺手加一行注释提醒未来升级时注意 API 兼容性,省得后面踩坑。
以上就是这次 Dubbo 服务重启时注册中心下线不及时问题的排查与修复记录,感谢观看;