Seata 動態配置訂閱與降級實現原理

Seata 的動態降級須要結合配置中心的動態配置訂閱功能。動態配置訂閱,即經過配置中心監聽訂閱,根據須要讀取已更新的緩存值,ZK、Apollo、Nacos 等第三方配置中心都有現成的監聽器可實現動態刷新配置;動態降級,即經過動態更新指定配置參數值,使得 Seata 可以在運行過程當中動態控制全局事務失效(目前只有 AT 模式有這個功能)。java

那麼 Seata 支持的多個配置中心是如何適配不一樣的動態配置訂閱以及如何實現降級的呢?下面從源碼的層面詳細給你們講解一番。spring

動態配置訂閱

Seata 配置中心有一個監聽器基準接口,它主要有一個抽象方法和 default 方法,以下:後端

io.seata.config.ConfigurationChangeListener緩存

該監聽器基準接口主要有兩個實現類型:數據結構

  1. 實現註冊配置訂閱事件監聽器:用於實現各類功能的動態配置訂閱,好比 GlobalTransactionalInterceptor 實現了 ConfigurationChangeListener,根據動態配置訂閱實現的動態降級功能;
  2. 實現配置中心動態訂閱功能與適配:對於目前尚未動態訂閱功能的 file 類型默認配置中心,能夠實現該基準接口來實現動態配置訂閱功能;對於阻塞訂閱須要另起一個線程去執行,這時候能夠實現該基準接口進行適配,還能夠複用該基準接口的線程池;以及還有異步訂閱,有訂閱單個 key,有訂閱多個 key 等等,咱們均可以實現該基準接口以適配各個配置中心。

這裏就用默認的 file 配置中心,以它的實現類 FileListener 舉例子,它的實現邏輯以下:app

如上,異步

  • dataId:爲訂閱的配置屬性;函數

  • listener:配置訂閱事件監聽器,用於將外部傳入的 listener 做爲一個 wrapper,執行真正的變動邏輯,這裏特別須要注意的是,該監聽器與 FileListener 一樣實現了 ConfigurationChangeListener 接口,只不過 FileListener 是用於給 file 提供動態配置訂閱功能,而 listener 用於執行配置訂閱事件源碼分析

  • executor:用於處理配置變動邏輯的線程池,在 ConfigurationChangeListener#onProcessEvent 方法中用到。線程

FileListener#onChangeEvent 方法的實現讓 file 具有了動態配置訂閱的功能,它的邏輯以下:

無限循環獲取訂閱的配置屬性當前的值,從緩存中獲取舊的值,判斷是否有變動,若是有變動就執行外部傳入 listener 的邏輯。

ConfigurationChangeEvent 用於保存配置變動的事件類,它的成員屬性以下:

這裏的 getConfig 方法是如何感知 file 配置的變動呢?咱們點進去,發現它最終的邏輯以下:

發現它是建立一個 future 類,而後包裝成一個 Runnable 放入線程池中異步執行,最後調用 get 方法阻塞獲取值,那麼咱們繼續往下看:

allowDynamicRefresh:動態刷新配置開關;

targetFileLastModified:file 最後更改的時間緩存。

以上邏輯:

獲取 file 最後更新的時間值 tempLastModified,而後對比對比緩存值 targetFileLastModified,若是 tempLastModified > targetFileLastModified,說明期間配置有更改過,這時就從新加載 file 實例,替換掉舊的 fileConfig,使得後面的操做可以獲取到最新的配置值。

添加一個配置屬性監聽器的邏輯以下:

configListenersMap 爲 FileConfiguration 的配置監聽器緩存,它的數據結構以下:

ConcurrentMap<String/*dataId*/, Set<ConfigurationChangeListener>> configListenersMap

從數據結構上可看出,每一個配置屬性可關聯多個事件監聽器。

最終執行 onProcessEvent 方法,這個是監聽器基準接口裏面的 default 方法,它會調用 onChangeEvent 方法,即最終會調用 FileListener 中的實現。

動態降級

有了以上的動態配置訂閱功能,咱們只須要實現 ConfigurationChangeListener 監聽器,就能夠作各類各類的功能,目前 Seata 只有動態降級有用到動態配置訂閱的功能。

在「Seata AT 模式啓動源碼分析」這篇文章中講到,Spring 集成 Seata 的項目中,在 AT 模式啓動時,會用 用GlobalTransactionalInterceptor 代替了被 GlobalTransactional 和 GlobalLock 註解的方法,GlobalTransactionalInterceptor 實現了 MethodInterceptor,最終會執行 invoker 方法,那麼想要實現動態降級,就能夠在這裏作手腳。

  • 在 GlobalTransactionalInterceptor 中加入一個成員變量:
private volatile boolean disable;

在構造函數中進行初始化賦值:

ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION(service.disableGlobalTransaction)這個參數目前有兩個功能:

  1. 在啓動時決定是否開啓全局事務;
  2. 在開啓全局事務後,決定是否降級。
  • 實現 ConfigurationChangeListener:

這裏的邏輯簡單,就是判斷監聽事件是否屬於 ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION 配置屬性,若是是,直接更新 disable 值。

  • 接下來在 GlobalTransactionalInterceptor#invoke 中作點手腳

如上,disable = true 時,不執行全局事務與全局鎖。

  • 配置中心訂閱降級監聽器

io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary

在 Spring AOP 進行 wrap 邏輯過程當中,當前配置中心將訂閱降級事件監聽器。

更多精彩文章請關注做者維護的公衆號「後端進階」,這是一個專一後端相關技術的公衆號。
關注公衆號並回復「後端」免費領取後端相關電子書籍。
歡迎分享,轉載請保留出處。

公衆號「後端進階」,專一後端技術分享!

相關文章
相關標籤/搜索