延遲發佈
如果您的服務需要預熱時間,例如初始化快取、等待相關資源到位等,則可以使用 delay 進行延遲暴露。在 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(),而應該全部通過 IoC 注入的方式使用 Spring Bean。
- 如果真的要調用 getBean(),可以將 Dubbo 配置放到 Spring 配置的最後加載。
- 如果不想依賴配置順序,可以使用
<dubbo:provider delay=”-1” />
,讓 Dubbo 在 Spring 容器初始化完成後再暴露服務。 - 如果大量使用 getBean(),相當於將 Spring 退化成了工廠模式,可以考慮將 Dubbo 的服務單獨隔離到一個獨立的 Spring 容器中。
基於 Spring 的 ContextRefreshedEvent 事件觸發暴露 ↩︎