1. 程式人生 > >Seata 動態配置訂閱與降級實現原理

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

Seata 的動態降級需要結合配置中心的動態配置訂閱功能。動態配置訂閱,即通過配置中心監聽訂閱,根據需要讀取已更新的快取值,ZK、Apollo、Nacos 等第三方配置中心都有現成的監聽器可實現動態重新整理配置;動態降級,即通過動態更新指定配置引數值,使得 Seata 能夠在執行過程中動態控制全域性事務失效(目前只有 AT 模式有這個功能)。

那麼 Seata 支援的多個配置中心是如何適配不同的動態配置訂閱以及如何實現降級的呢?下面從原始碼的層面詳細給大家講解一番。

動態配置訂閱

Seata 配置中心有一個監聽器基準介面,它主要有一個抽象方法和 default 方法,如下:

io.seata.config.ConfigurationChangeListener

該監聽器基準介面主要有兩個實現型別:

  1. 實現註冊配置訂閱事件監聽器:用於實現各種功能的動態配置訂閱,比如 GlobalTransactionalInterceptor 實現了 ConfigurationChangeListener,根據動態配置訂閱實現的動態降級功能;
  2. 實現配置中心動態訂閱功能與適配:對於目前還沒有動態訂閱功能的 file 型別預設配置中心,可以實現該基準介面來實現動態配置訂閱功能;對於阻塞訂閱需要另起一個執行緒去執行,這時候可以實現該基準介面進行適配,還可以複用該基準介面的執行緒池;以及還有非同步訂閱,有訂閱單個 key,有訂閱多個 key 等等,我們都可以實現該基準介面以適配各個配置中心。

這裡就用預設的 file 配置中心,以它的實現類 FileListener 舉例子,它的實現邏輯如下:

如上,

  • 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 邏輯過程中,當前配置中心將訂閱降級事件監聽器。

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

相關推薦

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

Seata 的動態降級需要結合配置中心的動態配置訂閱功能。動態配置訂閱,即通過配置中心監聽訂閱,根據需要讀取已更新的快取值,ZK、Apollo、Nacos 等第三方配置中心都有現成的監聽器可實現動態重新整理配置;動態降級,即通過動態更新指定配置引數值,使得 Seata 能夠在執行過程中動態控制全域性事務失效(

vue動態配置ip

考慮一個成品的專案會給到各地方進行部署,而每個地方的ip和埠均無法保證統一,為了抽離開發人員的工作,需要對專案進行一定的配置,配置後的專案,只需要修改打包後的配置檔案,填寫相關的ip和埠,即可實現專案的部署。 由於vue打包後會生成static包、index檔案,為了防止打包後混淆,可以在專案的

原始碼分析Dubbo 泛化呼叫泛化實現原理

   本文將重點分析Dubbo的兩個重要特性:泛化呼叫與泛化實現。    1、泛化引用:    通常是服務呼叫方沒有引入API包,也就不包含介面中的實體類,故服務呼叫方只能提供Map形式的資料,由服務提供者根據Map轉化成對應的實體。    2、泛化實現  

volatilesynchronized實現原理

------------------------------------------------------------------   剛開始認識volatile的時候,覺得對它的一些特性非常迷惑。比如:具有可見性,如果一個執行緒修改了volatile變數的值,那麼其它執行緒也會發現這一點;同時它又不

Java 8 動態型別語言Lambda表示式實現原理分析

Java 8支援動態語言,看到了很酷的Lambda表示式,對一直以靜態型別語言自居的Java,讓人看到了Java虛擬機器可以支援動態語言的目標。 import java.util.function.Consumer; public class Lambda { pub

struts2中,一個Form表單配置多個action實現原理及案例

一、原理說明 以登入註冊為例 login.jsp:Form表單中包含登入、註冊兩個按鈕。登入按鈕配置LoginAction.java,註冊按鈕配置RegistAction.java <%@ page language="java" content

hashmapHashtable實現原理淺析

HashMap和Hashtable的區別 兩者最主要的區別在於Hashtable是執行緒安全,而HashMap則非執行緒安全 Hashtable的實現方法裡面都添加了synchronized關鍵字來確保執行緒同步,因此相對而言HashMap效能會

zookeeper訂閱釋出實現

方式一: import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.Cur

AOP的實現原理—反射動態代理

        其實AOP的意思就是面向切面程式設計。OO注重的是我們解決問題的方法(封裝成Method),而AOP注重的是解決許多問題的方法中的共同點,是對OO思想的一種補充!還是拿人家經常舉的一個

Seata 配置中心實現原理

Seata 可以支援多個第三方配置中心,那麼 Seata 是如何同時相容那麼多個配置中心的呢?下面我給大家詳細介紹下 Seata 配置中心的實現原理。 配置中心屬性載入 在 Seata 配置中心,有兩個預設的配置檔案: file.conf 是預設的配置屬性,registry.conf 主要儲存第三方註冊中

多個datasource的配置實現原理

      一般情況下,一個專案中只會有一個datasource,但是在某些情況、或者業務需求的情況下會出現一個專案有多個datasource的情況,當滿足一定條件的時候,對資料庫的操作就會從一個一個datasource切換到另一個datasource. 那麼這種多資料來源的配置

Kafka動態配置實現原理解析

問題導讀 Apache Kafka在全球各個領域各大公司獲得廣泛使用,得益於它強大的功能和不斷完善的生態。其中Kafka動態配置是一個比較高頻好用的功能,下面我們就來一探究竟。 動態配置是如何設計的? 動態配置優先順序是怎樣的? Broker初始化是如何讀取配置的? 動態配置支援哪些特性功能? 動態配置如何

2-3-配置DHCP服務器實現動態地址分配

客戶端 -name sci oom 動態分配 工作站 request請求 負責 evel 學習一個服務的過程: 1、 此服務的概述:名字,功能,特點,端口號 2、 安裝 3、 配置文件的位置 4、 服務啟動關閉腳本,查看端口 5、 此服務的使用方法 6、 修

jQuery技術內幕:深入解析jQuery架構設計實現原理

源碼 att root 功能 技術內幕 瀏覽器 sel 緩存 callbacks jQuery源碼(jquery-1.7.1.js)的總體結構:(function( window, undefined ) {// 構造jQuery對象 var jQuery = (fun

ubuntu 12.04 簡單配置samba服務,實現主機虛擬機互通(設置Windows虛擬盤)

完成 sam inux ... conf restart bridged 要求 system 環境: virtualbox ubuntu12.04 首先,如果你到這步了,說明你的window與linux的網絡已經配好了,他們之間是可以互相Ping通的,如果沒有,請看我以

關於SPI通信原理程序實現

時鐘 程序 size mos pre img 平時 gpo 基於 第一次接觸SPI是因為當時用到NRF24L01,需要用SPI進行通信。因為2401上面寫著MOSI、MISO、SS、RST,當時以為只要用到SPI就肯定有這幾個引腳,以至於限制了自己的思維。只認識MISO/M

單點登錄原理簡單實現

title with 內部 resp 判斷 觀察 pac target sessionid 1、http無狀態協議 web應用采用browser/server架構,http作為通信協議。http是無狀態協議,瀏覽器的每一次請求,服務器會獨立處理,不與之前或之後的請求產生關聯

Win7 環境下虛擬機內 Samba 服務器的安裝、配置以及主機的通信實現

path 正常 驅動器 update tps RM 找到 samba配置 需要 考慮到window和linux虛擬機之間互傳文件較為麻煩,遂打算在虛擬機中安裝Samba服務器,以此實現共享文件給window使用。然而安裝配置過程曲折,遂作記錄如下: 一、samba服務器的安

(轉)HashMap底層實現原理/HashMapHashTable區別/HashMapHashSet區別

eem 實現原理 ger 銀行 索引 target 聲明 到你 們的 ①HashMap的工作原理 HashMap基於hashing原理,我們通過put()和get()方法儲存和獲取對象。當我們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算has

基於AOP註解實現業務功能的動態配置

引擎 sin equal cgroups list tro com pan inter 一、導入jar包 <dependency><!-- 4.引入AOP--> <groupId>org.springframework.boot