Nacos解讀:配置管理客戶端
上篇文章介紹了Nacos的服務發現(Naming)的客戶端,這篇來聊下配置管理(Config)的客戶端。
幾個概念
首先引用官方文件提到的幾個概念來幫助我們理解:
- 配置集 (Configuration Set):一組相關或者不相關的配置項的集合稱為配置集。在系統中,一個配置檔案通常就是一個配置集,包含了系統各個方面的配置。
- 配置集 ID(Data ID):Nacos 中的某個配置集的 ID。配置集 ID 是組織劃分配置的維度之一。Data ID 通常用於組織劃分系統的配置集。一個系統或者應用可以包含多個配置集,
- 配置分組(Group):Nacos 中的一組配置集,是組織配置的維度之一。通過一個有意義的字串(如 Buy 或 Trade )對配置集進行分組,從而區分 Data ID 相同的配置集。當您在 Nacos 上建立一個配置時,如果未填寫配置分組的名稱,則配置分組的名稱預設採用 DEFAULT_GROUP 。
此外,還有幾個後文會用到的名詞和變數需要說明一下:
- tenant:租戶資訊,等同於namespace。
- server_name:客戶端給Nacos服務端的名稱,根據配置情況可能有以下幾種可能。前兩種對應配置IP和埠的方式,後兩種對應配置endpoint的方式。
namespace | endpoint | 對應的server_name |
---|---|---|
未設定 | 未設定 | fixed-{IP1}_{port1}-{IP2}_{port2} |
設定 | 未設定 | fixed-{ip1}_{port1}-{ip2}_{port2}-{namespace} |
未設定 | 設定 | {endpoint} |
設定 | 設定 | {endpoint}-{namespace} |
Example
同樣,我們先來看官方示例。
String serverAddr = "localhost"; String dataId = "test"; String group = "DEFAULT_GROUP"; Properties properties = new Properties(); properties.put("serverAddr", serverAddr); ConfigService configService = NacosFactory.createConfigService(properties); String content = configService.getConfig(dataId, group, 5000); System.out.println(content); configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("receive:" + configInfo); } @Override public Executor getExecutor() { return null; } }); boolean isPublishOk = configService.publishConfig(dataId, group, "content"); System.out.println(isPublishOk); Thread.sleep(3000); content = configService.getConfig(dataId, group, 5000); System.out.println(content); boolean isRemoveOk = configService.removeConfig(dataId, group); System.out.println(isRemoveOk); Thread.sleep(3000); content = configService.getConfig(dataId, group, 5000); System.out.println(content); Thread.sleep(300000);
ConfigService
ConfigService
的實現類是 com.alibaba.nacos.client.config.NacosConfigService
,對外提供了以下方法:
getConfig addListener publishConfig removeConfig removeListener getServerStatus
接下來分別從幾個核心邏輯入手分析一下Config客戶端的實現。
獲取配置
呼叫 getConfig
方法會按照如圖所示的邏輯返回配置。

獲取配置的優先順序依次為:本地配置 -> 服務端獲取的配置 -> Snapshot儲存的配置。
本地配置和Snapshot是由 com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor
類來管理的。
優先使用本地配置,如果我們需要在某臺機器上使用跟線上環境不同的配置用於除錯等場景,可以通過在本地新增配置來達到目的。
預設路徑為 {user.home}/nacos/config/{server_name}_nacos/data/config-data[-tenant/{tenant}【存在tenant時才有這一部分】]/{group}/{dataId}
。
示例:
-
/Users/darkness463/nacos/config/fixed-localhost_8848_nacos/config-data/DEFAULT_GROUP/test
(未配置tenant) -
/Users/darkness463/nacos/config/fixed-localhost_8848_nacos/config-data-tenant/test_tenant/DEFAULT_GROUP/test
(tenant配置為test_tenant
)
服務端配置快照 (Configuration Snapshot)儲存的是上次從Nacos服務端獲取到的配置。
預設儲存路徑為 {user.home}/nacos/config/{server_name}_nacos/data/snapshot[-tenant/{tenant}【存在tenant時才有這一部分】]/{group}/{dataId}
。
示例:
-
/Users/darkness463/nacos/config/fixed-localhost_8848_nacos/snapshot/DEFAULT_GROUP/test
(未配置tenant) -
/Users/darkness463/nacos/config/fixed-localhost_8848_nacos/snapshot-tenant/test_tenant/DEFAULT_GROUP/test
(tenant配置為test_tenant
)
釋出配置
釋出配置邏輯比較簡單,即通過請求Nacos服務端釋出對應{tenant}、{dataId}、{group}的配置。
事實上,Nacos服務端還支援標籤、歸屬應用等額外資訊的配置,客戶端也有對應的程式碼實現,但在目前版本的客戶端中並未對外提供相應方法傳入這些引數,可能在後續版本中會提供吧。
監聽配置變化
初始化會啟動兩個執行緒池用於監聽配置的變化。一個是執行緒數為1的執行緒池,執行緒名為 com.alibaba.nacos.client.Worker.{server_name}
,每隔10毫秒會檢查需不需要啟動新任務來拉取配置。另一個執行緒池用於實際拉取配置,執行緒名為 com.alibaba.nacos.client.Worker.longPolling.{server_name}
。
CacheData
com.alibaba.nacos.client.config.impl.CacheData
這個類用於儲存客戶端設定的Listener資訊,所有的CacheData都儲存在 AtomicReference<Map<String, CacheData>> cacheMap
中,CacheData包含以下幾個比較重要的屬性:
private final String name; public final String dataId; public final String group; public final String tenant; private final CopyOnWriteArrayList<ManagerListenerWrap> listeners; private volatile String md5; private volatile boolean isUseLocalConfig = false; private volatile long localConfigLastModified; private volatile String content; private int taskId;
我們可以對相同的{dataId}、{group}、{tenant}設定不同的Listener,這些Listener會被儲存到同一個CacheData物件的 listeners
屬性中。
taskId
屬性用於確定向服務端定期拉取配置時的任務批次,在新建立CacheData時確定,其生成邏輯為:
int taskId = cacheMap.get().size() / (int)ParamUtil.getPerTaskConfigSize();
其中 ParamUtil.getPerTaskConfigSize()
的預設值為3000,也就是說每3000個CacheData會啟動一個任務,通過 com.alibaba.nacos.client.Worker.longPolling.{server_name}
執行緒來拉取配置。這個預設值可以通過在SystemProperties中設定key為 PER_TASK_CONFIG_SIZE
的值來改變。
LongPollingRunnable
LongPollingRunnable
即用於從服務端拉取配置變化的任務,每個 LongPollingRunnable
都有一個 taskId
用於標記任務的編號,與上面CacheData的 taskId
具有同樣的意義。
LongPollingRunnable
按照以下邏輯執行:

其中本地配置即上文提到的獲取配置時優先使用的本地配置,其變化情況分為:無->有,有->無,有變更這3種情況,根據變化情況會對CacheData中的 isUseLocalConfig
進行標記。對於使用本地配置的CacheData,將不會請求服務端。
同一批CacheData的資訊拼接起來請求Nacos服務端,定義了2個分隔符: WORD_SEPARATOR
和 LINE_SEPARATOR
, WORD_SEPARATOR
用於分隔同一個CacheData的不同資訊,值為 (char)2
, LINE_SEPARATOR
用於分隔不同的CacheData,值為 (char)1
。
拼接的格式為:{dataId}{WORD_SEPARATOR}{group}{WORD_SEPARATOR}{MD5}[{WORD_SEPARATOR}{tenant}【存在tenant時才有這一部分】]{LINE_SEPARATOR}{dataId}{WORD_SEPARATOR}{group}{WORD_SEPARATOR}{MD5}[{WORD_SEPARATOR}{tenant}【存在tenant時才有這一部分】]。
請求引數示例: Listening-Configs=dataId^2group^2contentMD5^2tenant^1
在請求的header中添加了 Long-Pulling-Timeout
的header,預設值為30000,即30秒。Nacos服務端採用了非同步Servlet,會比對請求的傳過去的MD5,如果有更新,會返回更新了的配置,如果沒有更新,會等待住30秒。
其他可配置項
除了前面已經提到的一些可配置項,還有一些可配置項可能在官方的文件裡並沒寫,但從程式碼可以看到一些可調整的配置,但有部分配置實際並沒有使用到,這裡只列出來用到的。
本地配置和Snapshot
預設情況下,本地配置和Snapshot的路徑字首是{user.home},可以通過在SystemProperties中設定key為 JM.SNAPSHOT.PATH
的值修改。
tenant
可以通過以下幾種方式設定tenant,按順序獲取。
- 在初始化
ConfigService
的Properties
中設定,key為namespace
。 - 在SystemProperties中設定,key為
tenant.id
。 - 在SystemProperties中設定,key為
acm.namespace
。
網路相關引數
- appKey:客戶端身份資訊,key為
nacos.client.appKey
。 - Nacos服務端埠:key為
nacos.server.port
,預設值8848。 - 連線超時時間:key為
NACOS.CONNECT.TIMEOUT
,預設值1000。 - 客戶端版本:從
application.properties
中獲取,key為version
。
總結
總體來說,配置管理(Config)的客戶端邏輯比較簡單,比較重要的只有監聽配置變化這部分。
到此我們已經深入瞭解了Nacos的客戶端,縱觀整個客戶端程式碼,服務發現(Naming)和配置管理(Config)似乎是強行整合到一起的,連Http相關的東西都用了兩套,還出現了 namespace
和 tenant
這樣一個概念兩種命名的情況。
建議要麼把服務發現(Naming)和配置管理(Config)的客戶端分開,要麼整合得更完美一些。
後續我們來看看Nacos客戶端是如何與Spring Cloud整合的。