延遲發佈

延遲暴露 Dubbo 服務

如果您的服務需要預熱時間,例如初始化快取、等待相關資源到位等,則可以使用 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() 的用法。

  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(),而應該全部通過 IoC 注入的方式使用 Spring Bean。
  2. 如果真的要調用 getBean(),可以將 Dubbo 配置放到 Spring 配置的最後加載。
  3. 如果不想依賴配置順序,可以使用 <dubbo:provider delay=”-1” />,讓 Dubbo 在 Spring 容器初始化完成後再暴露服務。
  4. 如果大量使用 getBean(),相當於將 Spring 退化成了工廠模式,可以考慮將 Dubbo 的服務單獨隔離到一個獨立的 Spring 容器中。

  1. 基於 Spring 的 ContextRefreshedEvent 事件觸發暴露 ↩︎


上次修改時間:2023 年 1 月 2 日:增強英文文件 (#1798) (95a9f4f6c1c)