延迟暴露

延迟暴露 Dubbo 服务

如果您的服务需要预热时间,例如初始化缓存,等待相关资源就位等,可以使用延迟进行延迟暴露。在 Dubbo 2.6.5 版本中,我们对服务延迟暴露逻辑进行了细微调整,将需要延迟暴露(delay > 0)的服务的倒计时动作推迟到 Spring 初始化完成之后。您在使用 Dubbo 时不会感受到这种变化,请放心使用。

Dubbo 2.6.5 之前的版本

延迟到 Spring 初始化完成之后再暴露服务1

<dubbo:service delay="-1" />

延迟 5 秒暴露服务

<dubbo:service delay="5000" />

Dubbo 2.6.5 及更高版本

所有服务将在 Spring 初始化完成之后暴露。如果您不需要延迟暴露服务,则无需配置 delay。

延迟 5 秒暴露服务

<dubbo:service delay="5000" />

Spring 2.x 初始化死锁问题

触发条件

当 Spring 解析到 <dubbo:service /> 时,服务已经暴露,Spring 还在初始化其他 bean。如果此时有请求进来,并且服务实现类中存在调用 applicationContext.getBean() 的使用。

  1. 请求线程的 applicationContext.getBean() 调用首先会同步 singletonObjects 判断 Bean 是否存在,不存在则初始化同步 beanDefinitionMap,之后再次同步 singletonObjects 写入 Bean 实例缓存。

    deadlock

  2. Spring 初始化线程,由于不需要判断 bean 是否存在,直接同步 beanDefinitionMap 进行初始化,并同步 singletonObjects 写入 bean 实例缓存。

    /user-guide/images/lock-init-context.jpg

    这样导致 getBean 线程先锁住 singletonObjects,再锁住 beanDefinitionMap,最后再次锁住 singletonObjects。Spring 初始化线程先锁住 beanDefinitionMap,再锁住 singletonObjects。反向锁导致线程死锁,无法提供服务,也无法启动。

解决方法

  1. 强烈建议不要在服务实现类中调用 applicationContext.getBean(),所有使用 Spring bean 都通过 IoC 注入。
  2. 如果真的需要调用 getBean(),可以将 Dubbo 配置加载到 Spring 的最后。
  3. 如果不想依赖配置顺序,可以使用 <dubbo:provider delay=”-1” /> 使 Dubbo 在 Spring 容器初始化完成之后再暴露服务。
  4. 如果大量使用 getBean(),相当于将 Spring 退化为工厂模式,可以将 Dubbo 的服务隔离到一个单独的 Spring 容器中。

  1. 基于 Spring 的 ContextRefreshedEvent 事件触发暴露 ↩︎


上次修改时间:2023 年 1 月 2 日:增强 en 文档 (#1798) (95a9f4f6c1c)