轉載於https://github.com/ctripcorp/apollo,by Ctrip, Inc.
Apollo核心概念之“Namespace”
1. 什么是Namespace?
Namespace是配置項的集合,類似於一個配置文件的概念。
2. 什么是“application”的Namespace?
Apollo在創建項目的時候,都會默認創建一個“application”的Namespace。顧名思義,“application”是給應用自身使用的,熟悉Spring Boot的同學都知道,Spring Boot項目都有一個默認配置文件application.yml。在這里application.yml就等同於“application”的Namespace。對於90%的應用來說,“application”的Namespace已經滿足日常配置使用場景了。
客戶端獲取“application” Namespace的代碼如下:
Config config = ConfigService.getAppConfig();
客戶端獲取非“application” Namespace的代碼如下:
Config config = ConfigService.getConfig(namespaceName);
3. Namespace的格式有哪些?
配置文件有多種格式,例如:properties、xml、yml、yaml、json等。同樣Namespace也具有這些格式。在Portal UI中可以看到“application”的Namespace上有一個“properties”標簽,表明“application”是properties格式的。
注:非properties格式的namespace,在客戶端使用時需要調用
ConfigService.getConfigFile(String namespace, ConfigFileFormat configFileFormat)
來獲取,如果使用Http接口直接調用時,對應的namespace參數需要傳入namespace的名字加上后綴名,如datasources.json。
4. Namespace的獲取權限分類
Namespace的獲取權限分為兩種:
- private (私有的)
- public (公共的)
這里的獲取權限是相對於Apollo客戶端來說的。
4.1 private權限
private權限的Namespace,只能被所屬的應用獲取到。一個應用嘗試獲取其它應用private的Namespace,Apollo會報“404”異常。
4.2 public權限
public權限的Namespace,能被任何應用獲取。
5. Namespace的類型
Namespace類型有三種:
- 私有類型
- 公共類型
- 關聯類型(繼承類型)
5.1 私有類型
私有類型的Namespace具有private權限。例如上文提到的“application” Namespace就是私有類型。
5.2 公共類型
5.2.1 含義
公共類型的Namespace具有public權限。公共類型的Namespace相當於游離於應用之外的配置,且通過Namespace的名稱去標識公共Namespace,所以公共的Namespace的名稱必須全局唯一。
5.2.2 使用場景
- 部門級別共享的配置
- 小組級別共享的配置
- 幾個項目之間共享的配置
- 中間件客戶端的配置
5.3 關聯類型
5.3.1 含義
關聯類型又可稱為繼承類型,關聯類型具有private權限。關聯類型的Namespace繼承於公共類型的Namespace,用於覆蓋公共Namespace的某些配置。例如公共的Namespace有兩個配置項
k1 = v1 k2 = v2
然后應用A有一個關聯類型的Namespace關聯了此公共Namespace,且覆蓋了配置項k1,新值為v3。那么在應用A實際運行時,獲取到的公共Namespace的配置為:
k1 = v3
k2 = v2
5.3.2 使用場景
舉一個實際使用的場景。假設RPC框架的配置(如:timeout)有以下要求:
- 提供一份全公司默認的配置且可動態調整
- RPC客戶端項目可以自定義某些配置項且可動態調整
- 如果把以上兩點要求去掉“動態調整”,那么做法很簡單。在rpc-client.jar包里有一份配置文件,每次修改配置文件然后重新發一個版本的jar包即可。同理,客戶端項目修改配置也是如此。
- 如果只支持客戶端項目可動態調整配置。客戶端項目可以在Apollo “application” Namespace上配置一些配置項。在初始化service的時候,從Apollo上讀取配置即可。這樣做的壞處就是,每個項目都要自定義一些key,不統一。
- 那么如何完美支持以上需求呢?答案就是結合使用Apollo的公共類型的Namespace和關聯類型的Namespace。RPC團隊在Apollo上維護一個叫“rpc-client”的公共Namespace,在“rpc-client” Namespace上配置默認的參數值。rpc-client.jar里的代碼讀取“rpc-client”Namespace的配置即可。如果需要調整默認的配置,只需要修改公共類型“rpc-client” Namespace的配置。如果客戶端項目想要自定義或動態修改某些配置項,只需要在Apollo 自己項目下關聯“rpc-client”,就能創建關聯類型“rpc-client”的Namespace。然后在關聯類型“rpc-client”的Namespace下修改配置項即可。這里有一點需要指出的,那就是rpc-client.jar是在應用容器里運行的,所以rpc-client獲取到的“rpc-client” Namespace的配置是應用的關聯類型的Namespace加上公共類型的Namespace。
5.4 例子
如下圖所示,有三個應用:應用A、應用B、應用C。
- 應用A有兩個私有類型的Namespace:application和NS-Private,以及一個關聯類型的Namespace:NS-Public。
- 應用B有一個私有類型的Namespace:application,以及一個公共類型的Namespace:NS-Public。
- 應用C只有一個私有類型的Namespace:application
5.4.1 應用A獲取Apollo配置
//application Config appConfig = ConfigService.getAppConfig(); appConfig.getProperty("k1", null); // k1 = v11 appConfig.getProperty("k2", null); // k2 = v21 //NS-Private Config privateConfig = ConfigService.getConfig("NS-Private"); privateConfig.getProperty("k1", null); // k1 = v3 privateConfig.getProperty("k3", null); // k3 = v4 //NS-Public,覆蓋公共類型配置的情況,k4被覆蓋 Config publicConfig = ConfigService.getConfig("NS-Public"); publicConfig.getProperty("k4", null); // k4 = v6 cover publicConfig.getProperty("k6", null); // k6 = v6 publicConfig.getProperty("k7", null); // k7 = v7
5.4.2 應用B獲取Apollo配置
//application Config appConfig = ConfigService.getAppConfig(); appConfig.getProperty("k1", null); // k1 = v12 appConfig.getProperty("k2", null); // k2 = null appConfig.getProperty("k3", null); // k3 = v32 //NS-Private,由於沒有NS-Private Namespace 所以獲取到default value Config privateConfig = ConfigService.getConfig("NS-Private"); privateConfig.getProperty("k1", "default value"); //NS-Public Config publicConfig = ConfigService.getConfig("NS-Public"); publicConfig.getProperty("k4", null); // k4 = v5 publicConfig.getProperty("k6", null); // k6 = v6 publicConfig.getProperty("k7", null); // k7 = v7
5.4.3 應用C獲取Apollo配置
//application Config appConfig = ConfigService.getAppConfig(); appConfig.getProperty("k1", null); // k1 = v12 appConfig.getProperty("k2", null); // k2 = null appConfig.getProperty("k3", null); // k3 = v33 //NS-Private,由於沒有NS-Private Namespace 所以獲取到default value Config privateConfig = ConfigService.getConfig("NS-Private"); privateConfig.getProperty("k1", "default value"); //NS-Public,公共類型的Namespace,任何項目都可以獲取到 Config publicConfig = ConfigService.getConfig("NS-Public"); publicConfig.getProperty("k4", null); // k4 = v5 publicConfig.getProperty("k6", null); // k6 = v6 publicConfig.getProperty("k7", null); // k7 = v7
5.4.4 ChangeListener
以上代碼例子中可以看到,在客戶端Namespace映射成一個Config對象。Namespace配置變更的監聽器是注冊在Config對象上。
所以在應用A中監聽application的Namespace代碼如下:
Config appConfig = ConfigService.getAppConfig(); appConfig.addChangeListener(new ConfigChangeListener() { public void onChange(ConfigChangeEvent changeEvent) { //do something } })
在應用A中監聽NS-Private的Namespace代碼如下:
Config privateConfig = ConfigService.getConfig("NS-Private"); privateConfig.addChangeListener(new ConfigChangeListener() { public void onChange(ConfigChangeEvent changeEvent) { //do something } })
在應用A、應用B、應用C中監聽NS-Public的Namespace代碼如下:
Config publicConfig = ConfigService.getConfig("NS-Public"); publicConfig.addChangeListener(new ConfigChangeListener() { public void onChange(ConfigChangeEvent changeEvent) { //do something } })