Spring Cloud Config提供了一種在分佈式系統中外部化配置服務器和客戶端的支持。配置服務器有一箇中心位置,管理全部環境下的應用的外部屬性。客戶端和服務器映射到相同Spring Eventment 和 PropertySrouce抽象的概念,因此很是適合Spring應用,但也能夠在任何語言開發的任何應用中使用。在一個應用從開發、測試到生產的過程當中,你能夠分別地管理開發、測試、生產環境的配置,而且在遷移的時候獲取相應的配置來運行。java
準備工做git
準備幾個配置文件,命名規範爲 項目名稱-環境名稱.yml。本文在git倉庫:https://github.com/xuwenjin中,新建目錄config-repo-xwj,建立如下幾個文件:
github
每一個文件內容以下:web
application.ymlspring
profile: profile-default
config-client.ymlapi
profile: config-client
config-client-dev.yml服務器
profile: dev
config-client-test.yml數據結構
profile: test
代碼示例app
建立一個Maven項目,在pom.xml文件中添加以下內容:eclipse
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <!-- 解決啓動報Caused by: java.lang.ClassNotFoundException: org.eclipse.jgit.api.TransportConfigCallback --> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit</artifactId> <version>4.9.0.201710071750-r</version> </dependency> </dependencies>
啓動類:
@SpringBootApplication @EnableConfigServer // 經過@EnableConfigServer註解激活配置服務 public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
配置文件:application.yml
server: port: 18083 spring: application: name: config-server #應用程序名稱 cloud: config: server: git: uri: https://github.com/xuwenjin/config-repo-xwj #git上,配置文件地址
這樣,一個Config Server就完成了。
測試工做
規則中的參數含義以下:
{application}
映射到客戶端的「spring.application.name」(項目名稱) {profile}
映射到客戶端上的「spring.profiles.active」(環境名稱) {label}
這是一個服務器端功能,標記「版本」的配置文件集(在git中,至關於分支名)啓動服務,也能夠看到匹配規則:
先試下 {application}/{profile}[/{label}] 這個規則,請求路徑是 http://localhost:18083/config-client/dev/master,返回了全部配置文件信息:
另外四種規則差很少,可以使用如下路徑來訪問config-client-dev.yml文件,並直接返回其中的內容:
http://localhost:18083/config-client-dev.yml
http://localhost:18083/master/config-client-dev.yml
http://localhost:18083/config-client-dev.properties
http://localhost:18083/master/config-client-dev.properties
若是想要訪問config-client-test.yml文件,只須要將上面的 dev 換成 test 就行。
可是若是匹配不上呢?結果以下:
能夠看到,當匹配不上時,會默認讀取application.yml文件。
Config Server服務端是支持熱加載的,即不重啓服務的狀況下更改配置文件信息,是能夠直接讀取的
源碼分析
當發送請求時(如:http://localhost:18083/master/config-client-dev.yml ),會在 EnvironmentController 中進行匹配。以下:
@RestController @RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}") public class EnvironmentController { private EnvironmentRepository repository; // ... @RequestMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" }) public ResponseEntity<String> labelledYaml(@PathVariable String name, @PathVariable String profiles, @PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception { // 校驗profiles是否含義"-",若是有則拋出異常 validateProfiles(profiles); // 獲取環境信息,即遠程配置文件數據 Environment environment = labelled(name, profiles, label); // 將environment對象轉爲Map Map<String, Object> result = convertToMap(environment); if (this.stripDocument && result.size() == 1 && result.keySet().iterator().next().equals("document")) { Object value = result.get("document"); if (value instanceof Collection) { return getSuccess(new Yaml().dumpAs(value, Tag.SEQ, FlowStyle.BLOCK)); } else { return getSuccess(new Yaml().dumpAs(value, Tag.STR, FlowStyle.BLOCK)); } } String yaml = new Yaml().dumpAsMap(result); if (resolvePlaceholders) { yaml = resolvePlaceholders(prepareEnvironment(environment), yaml); } return getSuccess(yaml); } // ... }
核心方法 labelled 中,會調用最核心的類 NativeEnvironmentRepositor 的 findOne 方法:
@RequestMapping("/{name}/{profiles}/{label:.*}") public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) { if (name != null && name.contains("(_)")) { // "(_)" is uncommon in a git repo name, but "/" cannot be matched // by Spring MVC name = name.replace("(_)", "/"); } if (label != null && label.contains("(_)")) { // "(_)" is uncommon in a git branch name, but "/" cannot be matched // by Spring MVC label = label.replace("(_)", "/"); } Environment environment = this.repository.findOne(name, profiles, label); return environment; }
findOne方法會從遠程git倉庫中獲取配置文件數據:
@Override public Environment findOne(String config, String profile, String label) { SpringApplicationBuilder builder = new SpringApplicationBuilder( PropertyPlaceholderAutoConfiguration.class); ConfigurableEnvironment environment = getEnvironment(profile); builder.environment(environment); builder.web(false).bannerMode(Mode.OFF); if (!logger.isDebugEnabled()) { builder.logStartupInfo(false); } String[] args = getArgs(config, profile, label); // 設置監聽器 builder.application() .setListeners(Arrays.asList(new ConfigFileApplicationListener())); ConfigurableApplicationContext context = builder.run(args); environment.getPropertySources().remove("profiles"); try { return clean(new PassthruEnvironmentRepository(environment).findOne(config, profile, label)); } finally { context.close(); } }
獲取的Environment對象,數據結構以下:
能夠看到,配置文件信息,會放在 PropertySources 對象中~