工程可以有點小亂,但配置不能含糊;
一、配置架構在微服務的代碼工程中,配置管理是一項複雜的事情,即需要做好各個環境的配置隔離措施,還需要确保生産環境的配置安全;如果劃分的微服務足夠多,還要考慮配置更新時的效率;
常規情況下,在配置管理的體系中,分為四個主要的環境:開發、測試、灰度、生産;通常來說除了運維團隊之外,其他人員沒有查看灰度和生産配置的權限,以此來保證配置的安全性;配置中心的服務也會搭建兩套:研發與生産各自獨立部署。
二、配置方式在項目中涉及到的配置非常多,類型也很多,但是從大的結構上可以分為:工程級、應用級、組件級三大塊;實際上這裡隻是單純的從代碼工程來看配置,如果把持續集成、自動化相關模塊也考慮進來的話,配置的管理會更加複雜;
站在開發的角度來看,主要還是對應用級的配置進行管理,而在微服務的架構下,多個不同的服務既有大量相同的配置,又存在各種差異化的自定義參數,所以在維護上有一定的複雜性;
在單服務的工程中,應用中隻會存在一個bootstrap.yml配置文件,配置内容基本就是服務名稱,和配置中心地址等常規内容,其他複雜的配置都被封閉維護,避免核心内容洩露引發安全問題;
配置主體通常會被拆分成如下幾個層次:環境控制,用來識别灰度和生産;應用基礎,管理和加載各個服務的通用配置;服務差異則配置在各自獨立的文件内;并且對多個配置進行分類分層管理;以此保證配置的安全和降低維護難度。
三、Nacos配置首先還是從bootstrap.yml文件作為切入點,以當下常見的Nacos組件為例,圍繞基礎原理作為思路,來分析服務工程是如何加載Nacos配置中心的參數;
spring:
profiles:
active: dev,facade
cloud:
nacos:
config:
prefix: application
server-addr: 127.0.0.1:8848
file-extension: yml
組件配置:配置邏輯中,單個服務端提供自身配置參數的信息,從上篇服務管理的源碼中發現,這是一種很常用的手段;需要基于這些信息去Nacos服務中加載配置;
@ConfigurationProperties("spring.cloud.nacos.config")
public class NacosConfigProperties {
public Properties assembleConfigServiceProperties() {
Properties properties = new Properties();
properties.put("serverAddr", Objects.toString(this.serverAddr, ""));
properties.put("namespace", Objects.toString(this.namespace, ""));
return properties ;
}
}
加載邏輯:服務啟動時,先基于相應參數讀取Nacos中配置的,然後解析數據并被Spring框架進行加載,加載的過程通過MapPropertySource類進行Key和Value的讀取;
public class NacosPropertySourceBuilder {
private List<PropertySource<?>> loadNacosData(String dataId, String group, String fileExtension) {
// 查詢配置
String data = this.configService.getConfig(dataId, group, this.timeout);
// 解析配置
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data, fileExtension);
}
}
請求服務:服務端與Nacos中心通過Http請求的方式進行交互,通過Get請求攜帶參數,調用Nacos中心服務,從而獲取相應配置;
public class ClientWorker implements Closeable {
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout) {
// 核心參數
Map<String, String> params = new HashMap<String, String>(3);
params.put("dataId", dataId);
params.put("group", group);
params.put("tenant", tenant);
// 執行請求
HttpRestResult<String> result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
}
}
從源碼的結構圖來看,配置文件的加載邏輯也是采用事件模型實現的,這在前面任務管理中有詳細的描述;配置的核心作用就是在程序啟動時進行加載引導,這裡關鍵要理解EnvironmentPostProcessor的接口設計;
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
// 基礎配置
private static final String DEFAULT_NAMES = "application";
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
static {
Set<String> filteredProperties = new HashSet<>();
filteredProperties.add("spring.profiles.active");
filteredProperties.add("spring.profiles.include");
LOAD_FILTERED_PROPERTY = Collections.unmodifiableSet(filteredProperties);
}
// 加載邏輯
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
});
}
}
EnvironmentPostProcessor接口:加載應用的自定義環境;
@FunctionalInterface
public interface EnvironmentPostProcessor {
void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}
SpringApplication:用于啟動和引導應用程序,提供創建程序上下文實例,初始化監聽器,容器刷新等核心邏輯,可以圍繞run()方法進行調試分析;
ConfigurableEnvironment:環境配置的核心接口,涉及到當前配置文件識别,即profiles.active;以及配置文件的解析能力,即PropertyResolver;在Loader内部類中提供了構造方法和加載邏輯的實現。
,