nacos是阿里18年開源的做爲配置中心及服務發現的中間件,本文主要討論其做爲配置中心的一些功能及實現。 下圖描述了spring cloud config Appollo Nacos三個配置中心的一些特性,我的比較傾向於nacos,由於nacos 部署、使用特別方便,跟spring整個生態無縫結合。以前使用過百度公司的disconf做爲配置中心,可是使用起來不是很方便,代碼陳舊複雜,何況百度公司也不作任何維護了,因此選擇了放棄,進而擁抱nacos。 java
在系統開發過程當中,開發者一般會將一些須要變動的參數、變量等從代碼中分離出來獨立管理,以獨立的配置文件的形式存在。目的是讓靜態的系統工件或者交付物(如 WAR,JAR 包等)更好地和實際的物理運行環境進行適配。配置管理通常包含在系統部署的過程當中,由系統管理員或者運維人員完成。配置變動是調整系統運行時的行爲的有效手段。web
配置管理spring
系統配置的編輯、存儲、分發、變動管理、歷史版本管理、變動審計等全部與配置相關的活動。數據庫
配置項編程
一個具體的可配置的參數與其值域,一般以 param-key=param-value 的形式存在。例如咱們常配置系統的日誌輸出級別(logLevel=INFO|WARN|ERROR) 就是一個配置項。bootstrap
配置集緩存
一組相關或者不相關的配置項的集合稱爲配置集。在系統中,一個配置文件一般就是一個配置集,包含了系統各個方面的配置。例如,一個配置集可能包含了數據源、線程池、日誌級別等配置項。bash
配置集 ID網絡
Nacos 中的某個配置集的 ID。配置集 ID 是組織劃分配置的維度之一。Data ID 一般用於組織劃分系統的配置集。一個系統或者應用能夠包含多個配置集,每一個配置集均可以被一個有意義的名稱標識。Data ID 一般採用類 Java 包(如 com.taobao.tc.refund.log.level)的命名規則保證全局惟一性。此命名規則非強制。架構
配置分組
Nacos 中的一組配置集,是組織配置的維度之一。經過一個有意義的字符串(如 Buy 或 Trade )對配置集進行分組,從而區分 Data ID 相同的配置集。當您在 Nacos 上建立一個配置時,若是未填寫配置分組的名稱,則配置分組的名稱默認採用 DEFAULT_GROUP 。配置分組的常見場景:不一樣的應用或組件使用了相同的配置類型,如 database_url 配置和 MQ_topic 配置。
配置快照
Nacos 的客戶端 SDK 會在本地生成配置的快照。當客戶端沒法鏈接到 Nacos Server 時,可使用配置快照顯示系統的總體容災能力。配置快照相似於 Git 中的本地 commit,也相似於緩存,會在適當的時機更新,可是並無緩存過時(expiration)的概念。
1.java項目
主要採用nacos提供的SDK,採用nacos-client 1.0.0版本
maven座標
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.0.0</version>
</dependency>
複製代碼
獲取配置
描述 用於服務啓動的時候從 Nacos 獲取配置
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException
複製代碼
dataId string 配置 ID
group string 配置分組
timeout long 讀取配置超時時間,單位ms,推薦值3000
返回值
string 配置值
請求示例
try {
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{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);
} catch (NacosException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
複製代碼
異常說明 讀取配置超時或網絡異常,拋出 NacosException 異常。
監聽配置 描述 若是但願 Nacos 推送配置變動,可使用 Nacos 動態監聽配置接口來實現。
public void addListener(String dataId, String group, Listener listener)
複製代碼
請求參數
dataId string 配置 ID
group string 配置分組
listener Listener 監聽器,配置變動進入監聽器的回調函數。
返回值
string 配置值,初始化或者配置變動的時候經過回調函數返回該值。
請求示例
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{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("recieve1:" + configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
複製代碼
2.spring web項目
maven座標
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-spring-context</artifactId>
<version>${latest.version}</version>
</dependency>
複製代碼
最新版本能夠在 maven 倉庫,地址:mvnrepository.com/artifact/co…
配置管理服務
添加 @EnableNacosConfig 註解啓用 Nacos Spring 的配置管理服務。
如下示例中,咱們使用 @NacosPropertySource 加載了 dataId 爲 example 的配置源,並開啓自動更新:
/**
* 添加 @EnableNacosConfig 註解啓用 Nacos Spring 的配置管理服務
*/
@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "192.168.10.105:8848"))
/**
* @NacosPropertySource 加載了 dataId 爲 example 的配置源
*
* <p>
* Nacos 控制檯添加配置:
* <p>
* Data ID:example
* <p>
* Group:DEFAULT_GROUP
* <p>
* 配置內容:useLocalCache=true
*/
@NacosPropertySource(dataId = "example", autoRefreshed = true)
public class NacosConfiguration {
}
複製代碼
獲取屬性值
經過 Nacos 的 @NacosValue 註解設置屬性值。
/**
* 經過 Nacos 的 @NacosValue 註解設置屬性值
*
*/
@Controller
@RequestMapping("config")
public class ConfigController {
@NacosInjected
private ConfigService configService;
@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
private String useLocalCache;
@RequestMapping(value = "/get", method = GET)
@ResponseBody
public String get() {
return useLocalCache;
}
}
複製代碼
測試
啓動 Tomcat,訪問http://localhost:8080/config/get嘗試獲取配置信息。
因爲此時還未發佈過配置,因此返回內容是 false。
在配置管理頁面修改dataId 爲example,內容爲useLocalCache=true
再次訪問 http://localhost:8080/config/get
此時返回內容爲true,說明程序中的useLocalCache值已經被動態更新了。
3.springBoot項目
maven座標
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>${latest.version}</version>
</dependency>
複製代碼
注意:版本 0.2.x.RELEASE 對應的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 對應的是 Spring Boot 1.x 版本。
配置管理服務 在 application.properties 中配置 Nacos server 的地址:
nacos.config.server-addr=192.168.10.105:8848
使用 @NacosPropertySource 加載 dataId 爲 example 的配置源,並開啓自動更新:
@SpringBootApplication
@NacosPropertySource(dataId = "example", autoRefreshed = true)
public class NacosConfigApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConfigApplication.class, args);
}
}
複製代碼
獲取屬性值 經過 Nacos 的 @NacosValue 註解設置屬性值
@Controller
@RequestMapping("config")
public class ConfigController {
@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
private boolean useLocalCache;
@RequestMapping(value = "/get", method = GET)
@ResponseBody
public boolean get() {
return useLocalCache;
}
}
複製代碼
測試
啓動 NacosConfigApplication,訪問 http://localhost:8080/config/get
返回內容是 false。
在配置管理頁面修改dataId 爲example,內容爲useLocalCache=true
再次訪問 http://localhost:8080/config/get
此時返回內容爲true,說明程序中的useLocalCache值已經被動態更新了。
4.spring cloud:
引入依賴:
依賴的版本0.1.x對應spring boot 1.x,0.2.x對應spring boot 2.x,而且log4j2日誌的版本須要在2.7.0以上,推薦2.10.0版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>0.1.1.RELEASE</version>
</dependency>
複製代碼
增長配置:
spring.cloud.nacos.config.context-path=nacos
spring.cloud.nacos.config.shared-dataids=common.properties
spring.cloud.nacos.config.refreshable-dataids=common.properties
spring.cloud.nacos.config.ext-config[0].dataId=ext.properties
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.namespace=af0a428a-ff43-4971-8b3d-4eaf4ffcfbc9
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
複製代碼
如上配置須要添加到bootstrap配置文件纔可生效,可定義shared-dataids獲取一些公共配置,使用ext-config獲取額外配置,經過prefix和file-extension肯定dataId,以上配置的namespace會做用於全部shared、 ext和應用的dataId,不像spring boot方式,不一樣的NacosPropertySource註解中的namespace會分別生效
spring bean增長@RefreshScope,經過@Value(value = "${xx}") 注入配置,該方式能夠達到實時刷新效果,spring cloud經過監聽變動會銷燬bean,下次使用生成bean時從新注入屬性達到刷新效果。
登陸管理 Nacos1.0.1 版本支持簡單登陸功能,默認用戶名/密碼爲: nacos/nacos。
命名空間管理 Nacos 基於Namespace 幫助用戶邏輯隔離多個命名空間,這能夠幫助用戶更好的管理測試、預發、生產等多環境服務和配置,
讓每一個環境的同一個配置(如數據庫數據源)能夠定義不一樣的值。
配置管理 Nacos支持基於Namespace和Group的配置分組管理,以便用戶更靈活的根據本身的須要按照環境或者應用、
模塊等分組管理微服務以及Spring的大量配置,在配置管理中主要提供了配置歷史版本、回滾、訂閱者查詢等核心管理能力。
多配置格式編輯器
Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常見配置格式在線編輯、語法高亮、格式校驗,幫助用戶高效編輯的同時大幅下降格式錯誤帶來的風險。
Nacos支持配置標籤的能力,幫助用戶更好、更靈活的作到基於標籤的配置分類及管理。同時支持用戶對配置及其變動進行描述,方面多人或者跨團隊協做管理配置。
編輯DIFF Nacos支持編輯DIFF能力,幫助用戶校驗修改內容,下降改錯帶來的風險。
示例代碼 Nacos提供示例代碼能力,可以讓新手快速使用客戶端編程消費該配置,大幅下降新手使用門檻。
監聽者查詢 Nacos提供配置訂閱者即監聽者查詢能力,同時提供客戶端當前配置的MD5校驗值,以便幫助用戶更好的檢查配置變動是否推送到 Client 端。
配置的版本及一鍵回滾 Nacos經過提供配置版本管理及其一鍵回滾能力,幫助用戶改錯配置的時候可以快速恢復,下降微服務系統在配置管理上的必定會遇到的可用性風險。
client拉取配置和感知配置變動的核心接口是ConfigService,定義了發佈、刪除、獲取配置,添加刪除監聽器、獲取server狀態這些接口
String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
void addListener(String dataId, String group, Listener listener) throws NacosException;
boolean publishConfig(String dataId, String group, String content) throws NacosException;
boolean removeConfig(String dataId, String group) throws NacosException;
void removeListener(String dataId, String group, Listener listener);
String getServerStatus();
複製代碼
實現類NacosConfigService對配置的增刪改查操做經過一個HttpAgent來訪問server暴露的http服務來實現,NacosConfigService內部經過ClientWorker類來對不一樣http請求獲取的配置進行check刷新
final ScheduledExecutorService executor;
final ExecutorService executorService;
/**
* groupKey -> cacheData
*/
AtomicReference<Map<String, CacheData>> cacheMap = new AtomicReference<Map<String, CacheData>>(
new HashMap<String, CacheData>());
複製代碼
ClientWorker內部的核心成員如上,cacheMap 維護了不一樣groupKey(經過dataId,group,namespace組成)到CacheData(配置數據)映射,每次註冊監聽器以後把對應配置信息放入這個map,以後經過executorService每10ms提交一個LongPollingRunnable的線程
executor.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
checkConfigInfo();
} catch (Throwable e) {
LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
}
}
}, 1L, 10L, TimeUnit.MILLISECONDS);
複製代碼
checkConfigInfo方法以下
public void checkConfigInfo() {
// 分任務
int listenerSize = cacheMap.get().size();
// 向上取整爲批數
int longingTaskCount = (int)Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
if (longingTaskCount > currentLongingTaskCount) {
for (int i = (int)currentLongingTaskCount; i < longingTaskCount; i++) {
// 要判斷任務是否在執行 這塊須要好好想一想。 任務列表如今是無序的。變化過程可能有問題
executorService.execute(new LongPollingRunnable(i));
}
currentLongingTaskCount = longingTaskCount;
}
}
複製代碼
經過executorService提交一個長輪詢檢查線程,代碼比較長就不貼了,流程就是對每一個CacheData(配置數據),檢查配置當前版本跟本地配置文件最後變動時間是否一致肯定配置是否刷新,刷新則通知監聽器作對應處理,而後使用http請求(默認超時30s)server端這個地址/v1/cs/config/listener(長輪詢),server端這個地址會保持鏈接,若是配置沒有變動,請求會在停留30s後返回,若是有配置更變,請求會立刻返回,client接收到配置變動,會更新本地配置文件而且更新版本號。
server端經過LongPollingService來完成保持鏈接的能力,核心成員以下:
final ScheduledExecutorService scheduler;
/**
* 長輪詢訂閱關係
*/
final Queue<ClientLongPolling> allSubs;
複製代碼
ClientLongPolling是一個線程,經過scheduler默認30s定時處理請求,若是server端接收到配置變動請求,則經過發佈LocalDataChangeEvent事件,LongPollingService繼承監聽器,觸發監聽執行DataChangeTask,內部經過allSubs維護的異步請求上下文AsyncContext直接返回response。
spring boot集成nacos經過NacosPropertySourcePostProcessor,該類實現BeanFactoryPostProcessor及EnvironmentAware,經過nacos client獲取配置放入spring 上下文,經過是否刷新來決定是否註冊監聽。
spring cloud經過NacosPropertySourceLocator,在PropertySourceBootstrapConfiguration這個ApplicationContextInitializer里加載屬性源,監聽ApplicationReadyEvent事件後爲屬性源註冊監聽處理變動。
server端本地可經過standalone模式啓動,也可源碼直接運行,方便調試,client使用也很方便,支持配置分組和命名空間的隔離,支持配置刷新回滾,配置灰髮功能還有待開發,權限管理不足,整體使用仍是很是簡單方便,spring 生態集成度很高,比較推薦使用。