延迟暴露
如果您的服务需要预热时间,例如初始化缓存,等待相关资源就位等,可以使用延迟进行延迟暴露。在 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()
的使用。
请求线程的 applicationContext.getBean() 调用首先会同步 singletonObjects 判断 Bean 是否存在,不存在则初始化同步 beanDefinitionMap,之后再次同步 singletonObjects 写入 Bean 实例缓存。
Spring 初始化线程,由于不需要判断 bean 是否存在,直接同步 beanDefinitionMap 进行初始化,并同步 singletonObjects 写入 bean 实例缓存。
这样导致 getBean 线程先锁住 singletonObjects,再锁住 beanDefinitionMap,最后再次锁住 singletonObjects。Spring 初始化线程先锁住 beanDefinitionMap,再锁住 singletonObjects。反向锁导致线程死锁,无法提供服务,也无法启动。
解决方法
- 强烈建议不要在服务实现类中调用 applicationContext.getBean(),所有使用 Spring bean 都通过 IoC 注入。
- 如果真的需要调用 getBean(),可以将 Dubbo 配置加载到 Spring 的最后。
- 如果不想依赖配置顺序,可以使用
<dubbo:provider delay=”-1” />
使 Dubbo 在 Spring 容器初始化完成之后再暴露服务。 - 如果大量使用 getBean(),相当于将 Spring 退化为工厂模式,可以将 Dubbo 的服务隔离到一个单独的 Spring 容器中。
基于 Spring 的 ContextRefreshedEvent 事件触发暴露 ↩︎