Seata 能夠支持多個第三方配置中心,那麼 Seata 是如何同時兼容那麼多個配置中心的呢?下面我給你們詳細介紹下 Seata 配置中心的實現原理。java
在 Seata 配置中心,有兩個默認的配置文件:redis
file.conf 是默認的配置屬性,registry.conf 主要存儲第三方註冊中心與配置中心的信息,主要有兩大塊:json
registry { # file 、nacos 、eureka、redis、zk、consul、etcd三、sofa # ... } config { # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { serverAddr = "localhost" namespace = "" } file { name = "file.conf" } # ... }
其中 registry 爲註冊中心的配置屬性,這裏先不講,config 爲配置中心的屬性值,默認爲 file 類型,即會加載本地的 file.conf 裏面的屬性,若是 type 爲其它類型,那麼會從第三方配置中心加載配置屬性值。後端
在 config 模塊的 core 目錄中,有個配置工廠類 ConfigurationFactory,它的結構以下:ide
能夠看到都是一些配置的靜態常量:ui
REGISTRY_CONF_PREFIX、REGISTRY_CONF_SUFFIX:配置文件名、默認配置文件類型;spa
SYSTEM_PROPERTY_SEATA_CONFIG_NAME、ENV_SEATA_CONFIG_NAME、ENV_SYSTEM_KEY、ENV_PROPERTY_KEY:自定義文件名配置變量,也說明咱們能夠自定義配置中心的屬性文件。code
ConfigurationFactory 裏面有一處靜態代碼塊,以下:server
io.seata.config.ConfigurationFactory對象
根據自定義文件名配置變量找出配置文件名稱與類型,若是沒有配置,默認使用 registry.conf,FileConfiguration 是 Seata 默認的配置實現類,若是爲默認值,則會更具 registry.conf 配置文件生成 FileConfiguration 默認配置對象,這裏也能夠利用 SPI 機制支持第三方擴展配置實現,具體實現是繼承 ExtConfigurationProvider 接口,在META-INF/services/
建立一個文件並填寫實現類的全路徑名,以下所示:
在靜態代碼塊邏輯加載完配置中心屬性以後,Seata 是如何選擇配置中心並獲取配置中心的屬性值的呢?
咱們剛剛也說了 FileConfiguration 是 Seata 的默認配置實現類,它繼承了 AbstractConfiguration,它的基類爲 Configuration,提供了獲取參數值的方法:
short getShort(String dataId, int defaultValue, long timeoutMills); int getInt(String dataId, int defaultValue, long timeoutMills); long getLong(String dataId, long defaultValue, long timeoutMills); // ....
那麼意味着只須要第三方配置中心實現該接口,就能夠整合到 Seata 配置中心了,下面我拿 zk 來作例子:
首先,第三方配置中心須要實現一個 Provider 類:
實現的 provider 方法如其名,主要是輸出具體的 Configuration 實現類。
那麼咱們是如何獲取根據配置去獲取對應的第三方配置中心實現類呢?
在 Seata 項目中,獲取一個第三方配置中心實現類一般是這麼作的:
Configuration CONFIG = ConfigurationFactory.getInstance();
在 getInstance() 方法中主要是使用了單例模式構造配置實現類,它的構造具體實現以下:
io.seata.config.ConfigurationFactory#buildConfiguration:
首先從 ConfigurationFactory 中的靜態代碼塊根據 registry.conf 建立的 CURRENT_FILE_INSTANCE 中獲取當前環境使用的配置中心,默認爲爲 File 類型,咱們也能夠在 registry.conf 配置其它第三方配置中心,這裏也是利用了 SPI 機制去加載第三方配置中心的實現類,具體實現以下:
如上,便是剛剛我所說的 ZookeeperConfigurationProvider 配置實現輸出類,咱們再來看看這行代碼:
EnhancedServiceLoader.load(ConfigurationProvider.class,Objects.requireNonNull(configType).name()).provide();
EnhancedServiceLoader 是 Seata SPI 實現核心類,這行代碼會加載 META-INF/services/
和 META-INF/seata/
目錄中文件填寫的類名,那麼若是其中有多個配置中心實現類都被加載了怎麼辦呢?
咱們注意到 ZookeeperConfigurationProvider 類的上面有一個註解:
@LoadLevel(name = "ZK", order = 1)
在加載多個配置中心實現類時,會根據 order 進行排序:
io.seata.common.loader.EnhancedServiceLoader#findAllExtensionClass:
io.seata.common.loader.EnhancedServiceLoader#loadFile:
這樣,就不會產生衝突了。
可是咱們發現 Seata 還能夠用這個方法進行選擇,Seata 在調用 load 方法時,還傳了一個參數:
Objects.requireNonNull(configType).name()
ConfigType 爲配置中心類型,是個枚舉類:
public enum ConfigType { File, ZK, Nacos, Apollo, Consul, Etcd3, SpringCloudConfig, Custom; }
咱們注意到,LoadLevel 註解上還有一個 name 屬性,在進行篩選實現類時,Seata 還作了這個操做:
根據當前 configType 來判斷是否等於 LoadLevel 的 name 屬性,若是相等,那麼就是當前配置的第三方配置中心實現類。
ZookeeperConfiguration 繼承了 AbstractConfiguration,它的構造方法以下:
構造方法建立了一個 zkClient 對象,這裏的 FILE_CONFIG 是什麼呢?
private static final Configuration FILE_CONFIG = ConfigurationFactory.CURRENT_FILE_INSTANCE;
原來就是剛剛靜態代碼塊中建立的 registry.conf 配置實現類,從該配置實現類拿到第三方配置中心的相關屬性,構造第三方配置中心客戶端,而後實現 Configuration 接口時:
就能夠利用客戶端相關方法去第三方配置獲取對應的參數值了。
上週末才寫好,已經提交 PR 上去了,還處於 review 中,預估會在 Seata 1.0 版本提供給你們使用,敬請期待。
具體位置在 Seata 項目的 script 目錄中:
config.txt 爲本地配置好的值,搭建好第三方配置中心以後,運行腳本會將 config.txt 的配置同步到第三方配置中心。
更多精彩文章請關注做者維護的公衆號「後端進階」,這是一個專一後端相關技術的公衆號。
關注公衆號並回復「後端」免費領取後端相關電子書籍。
歡迎分享,轉載請保留出處。