Seata 可以支持多個第三方配置中心,那麼 Seata 是如何同時兼容那麼多個配置中心的呢?下面我給大家詳細介紹下 Seata 配置中心的實現原理。
配置中心屬性加載
在 Seata 配置中心,有兩個默認的配置文件:
file.conf 是默認的配置屬性,registry.conf 主要存儲第三方註冊中心與配置中心的信息,主要有兩大塊:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、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,它的結構如下:
可以看到都是一些配置的靜態常量:
REGISTRY_CONF_PREFIX、REGISTRY_CONF_SUFFIX:配置文件名、默認配置文件類型;
SYSTEM_PROPERTY_SEATA_CONFIG_NAME、ENV_SEATA_CONFIG_NAME、ENV_SYSTEM_KEY、ENV_PROPERTY_KEY:自定義文件名配置變量,也說明我們可以自定義配置中心的屬性文件。
ConfigurationFactory 裏面有一處靜態代碼塊,如下:
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 的配置同步到第三方配置中心。
更多精彩文章請關注作者維護的公衆號「後端進階」,這是一個專注後端相關技術的公衆號。
關注公衆號並回復「後端」免費領取後端相關電子書籍。
歡迎分享,轉載請保留出處。