Dubbo服务重启时注册中心下线不及时问题排查与修复记录

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 版本对下线机制做了明显的优化:

  1. 新增 DubboShutdownHook 类,把主要的下线逻辑收拢进去
  2. 2.6.2 原有的下线方法被移除,只保留了兼容调用
  3. 新增 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 服务重启时注册中心下线不及时问题的排查与修复记录,感谢观看;

相关推荐
confiself1 小时前
树形思考研究进展
java·大数据·微服务
还是鼠鼠1 小时前
【Spring AI智能体实战 01】Java开发者接入大模型:国内平台、Token与API Key入门
java·大模型·spring ai·deepseek·阿里云百炼
zerozrc02 小时前
Java Lambda 底层实现
java
AlunYegeer2 小时前
Spring Boot 多模块服务间 API Token 鉴权排查记录
java·spring boot·后端
编程风暴2 小时前
3类人群的数据分析实习类型选择+4条选错红线
java·前端·数据分析
钱六两2 小时前
Spring AI 使用 MCP 客户端(调用高德 MCP)
java·人工智能·spring
城管不管2 小时前
RabbitMQ死信队列
java·分布式·ai·面试·职场和发展·rabbitmq·agent
long3162 小时前
Java 团队入门到精通学习资料
java·开发语言
vx-程序开发2 小时前
【java项目分享】springboot农产品销售平台14952
java·javascript·spring boot·python·eclipse·django·php