1. 程式人生 > >spring cloud連載第二篇之Spring Cloud Config

spring cloud連載第二篇之Spring Cloud Config

Spring Cloud Config

Spring Cloud Config為分散式服務提供了服務側和客戶側的外部配置支援。通過Spring Cloud Config你可以有一個統一的地方來管理所有應用的外部配置。

預設服務端儲存實現用的是git,因此,它很容易支援配置環境的標籤版本,並且可以訪問各種管理內容的工具。

1. Quick Start

此小結將介紹Spring Cloud Config Server的客戶端和服務端。

首先,啟動服務端:

1 $ cd spring-cloud-config-server
2 $ ../mvnw spring-boot:run

接著試一下客戶端:

1 $ curl localhost:8888/foo/development
2 {"name":"foo","label":"master","propertySources":[
3   {"name":"https://github.com/scratches/config-repo/foo-development.properties","source":{"bar":"spam"}},
4   {"name":"https://github.com/scratches/config-repo/foo.properties","source":{"foo":"bar"}}
5 ]}

預設定位屬性資源的策略是使用git(spring.cloud.config.server.git.uri),並且用它來初始化一個迷你SpringApplication,這個迷你application的Environment是用來列舉屬性資源並將它釋出到一個JSON端點。

HTTP服務的格式如下:

1 /{application}/{profile}[/{label}]
2 /{application}-{profile}.yml
3 /{label}/{application}-{profile}.yml
4 /{application}-{profile}.properties
5 /{label}/{application}-{profile}.properties

其中application是是使用spring.config.name屬性注入的,profile是一個啟用的配置檔案(或者是以逗號分隔的屬性),label是一個可選的git標籤(預設是master)。

Spring Cloud Config Server從git倉庫為客戶端拉取配置,如下:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/spring-cloud-samples/config-repo

1.1 Client Side Usage(客戶端用法)

要在一個應用中使用這些特性,你可以構建一個Spring Boot application並且新增spring-cloud-config-client相關的依賴,最方便的方法是新增org.springframework.cloud:spring-cloud-starter-config依賴。

下面是一個典型的maven配置:

 1 <parent>
 2        <groupId>org.springframework.boot</groupId>
 3        <artifactId>spring-boot-starter-parent</artifactId>
 4        <version>{spring-boot-docs-version}</version>
 5        <relativePath /> <!-- lookup parent from repository -->
 6    </parent>
 7 
 8 <dependencyManagement>
 9     <dependencies>
10         <dependency>
11             <groupId>org.springframework.cloud</groupId>
12             <artifactId>spring-cloud-dependencies</artifactId>
13             <version>{spring-cloud-version}</version>
14             <type>pom</type>
15             <scope>import</scope>
16         </dependency>
17     </dependencies>
18 </dependencyManagement>
19 
20 <dependencies>
21     <dependency>
22         <groupId>org.springframework.cloud</groupId>
23         <artifactId>spring-cloud-starter-config</artifactId>
24     </dependency>
25     <dependency>
26         <groupId>org.springframework.boot</groupId>
27         <artifactId>spring-boot-starter-test</artifactId>
28         <scope>test</scope>
29     </dependency>
30 </dependencies>
31 
32 <build>
33     <plugins>
34            <plugin>
35                <groupId>org.springframework.boot</groupId>
36                <artifactId>spring-boot-maven-plugin</artifactId>
37            </plugin>
38     </plugins>
39 </build>

現在可以建立一個標準的Spring Boot application,如下:

 1 @SpringBootApplication
 2 @RestController
 3 public class Application {
 4 
 5     @RequestMapping("/")
 6     public String home() {
 7         return "Hello World!";
 8     }
 9 
10     public static void main(String[] args) {
11         SpringApplication.run(Application.class, args);
12     }
13 
14 }

當這個HHTP服務啟動時,它預設從本地的config server(埠8888)載入配置,可以使用bootstrap.properties配置來改變這個預設行為:

1 spring.cloud.config.uri: http://myconfigserver.com

可以使用/env端點來檢視bootstrap properties配置:

1 $ curl localhost:8080/env
2 {
3   "profiles":[],
4   "configService:https://github.com/spring-cloud-samples/config-repo/bar.properties":{"foo":"bar"},
5   "servletContextInitParams":{},
6   "systemProperties":{...},
7   ...
8 }

2. Spring Cloud Config Server(配置伺服器的服務端)

Spring Cloud Config Server為外部配置提供一個基於資源的HTTP API。使用註解@EnableConfigServer可以在一個Spring Boot application中嵌入配置伺服器。例如:

1 @SpringBootApplication
2 @EnableConfigServer
3 public class ConfigServer {
4   public static void main(String[] args) {
5     SpringApplication.run(ConfigServer.class, args);
6   }
7 }

最簡單的方法是通過屬性spring.config.name=configserver啟動,因為在config server的jar中有一個configserver.yml配置檔案,它已經設定好了預設的配置倉庫。

或者你可以使用自己的配置:application.properties

1 server.port: 8888
2 spring.cloud.config.server.git.uri: file://${user.home}/config-repo

其中${user.home}/config-repo是一個包含YAML或者properties格式配置檔案的git倉庫。

注意:

1)在windows上,你需要在file url前多新增一個“/”,如果是絕對路徑的話。(例如:file:///${user.home}/config-repo

2)使用本地倉庫只是為了演示和測試使用,在正式環境中需要使用一臺伺服器來管理配置

3)初始化克隆配合倉庫在只有一個檔案時可能比較快,但是如果儲存一個二進位制檔案,特別是比較大的檔案時,可能在第一次請求配置時有延遲甚至可能導致服務端記憶體溢位。

2.1 Environment Repository

在config server中我們應該將配置儲存在什麼地方?掌控這個行為的策略的就是為Environment物件服務的EnvironmentRepository。

Environment資源由一下三個變數引數化:

1){application} 對應客戶端的spring.application.name

2){profile} 對應客戶端的spring.profiles.active(以逗號分隔)

3){label} 它是伺服器端功能標籤“版本化”的配置檔案集

Repository的實現通常像一個spring boot application,從一個和{application}相等的spring.config.name引數和與{profiles}相等的spring.profiles.active引數指定的檔案來載入配置。

優先順序策略和普通spring boot 的應用一樣:Active profiles優先順序比預設的高,如果有多個profiles,那麼最後一個優先順序最高。下面是客戶端bootstrap 配置bootstrap.yml:

1 spring:
2   application:
3     name: foo
4   profiles:
5     active: dev,mysql

(和一般Spring Boot application一樣,這些引數也可以在環境變數或者命令列引數中指定)

2.1.1 Git Backend

在config server中使用spring.cloud.config.server.git.uri來設定倉庫位置,如果uri是以file:開頭,則是本地倉庫。

但是如果要擴充套件config server並且高可用的話,必須使所有伺服器例項指向同一個倉庫。因此需要一個共享的檔案系統。在這種情況下,使用ssh協議是比較好的,這樣伺服器可以克隆一份配置到本地作為快取使用。

倉庫的實現是將HTTP路徑中的{label}對映到一個git標籤(commit id, branch name, or tag),如果一個git分支或者標籤中包含“/”斜槓,那麼在HTTP url中需要用一個特殊字元(_)來替代,以免發生混淆。

比如標籤為foo/bar,則應該替換成:foo(_)bar。這個規則同樣可應用到{application}引數上。

Skipping SSL Certificate Validation(跳過ssl驗證)

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://example.com/my/repo
7           skipSslValidation: true

Setting HTTP Connection Timeout(設定HTTP連線超時時間,以秒為單位)

spring:
  cloud:
    config:
      server:
        git:
          uri: https://example.com/my/repo
          timeout: 4

Placeholders in Git URI(git URI中的佔位符)

Spring Cloud Config Server支援git URI中的佔位符為:{application},{profile},{label}(記住label是作為git 的標籤使用的)。通過下面的配置你可以支援每個應用一個倉庫的策略:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/myorg/{application}

Pattern Matching and Multiple Repositories(規則匹配和多倉庫)

Spring Cloud Config包含對application和profile名稱的規則匹配支援,規則格式是包含萬用字元的以逗號分隔的{application}/{profile}名稱。例如:

 1 spring:
 2   cloud:
 3     config:
 4       server:
 5         git:
 6           uri: https://github.com/spring-cloud-samples/config-repo
 7           repos:
 8             simple: https://github.com/simple/config-repo
 9             special:
10               pattern: special*/dev*,*special*/dev*
11               uri: https://github.com/special/config-repo
12             local:
13               pattern: local*
14               uri: file:/home/configsvc/config-repo

如果{application}/{profile}不匹配任何一個規則,則使用spring.cloud.config.server.git.uri下面的預設URI。在上面的例子中,匹配“simple”倉庫的規則是simple/*({application}為simple,{profile}為任意值)。“local”倉庫匹配任何一個以local開頭的應用名稱,如果沒有指定{profile}規則,則會自動在規則後面新增“/*”。

由於pattern引數實際上是一個數組,所以使用YAML陣列(或者在.properties檔案中使用[0], [1]等字尾)來繫結多種規則。如下:

 1 spring:
 2   cloud:
 3     config:
 4       server:
 5         git:
 6           uri: https://github.com/spring-cloud-samples/config-repo
 7           repos:
 8             development:
 9               pattern:
10                 - '*/development'
11                 - '*/staging'
12               uri: https://github.com/development/config-repo
13             staging:
14               pattern:
15                 - '*/qa'
16                 - '*/production'
17               uri: https://github.com/staging/config-repo

每個倉庫都可以在子目錄中儲存配置檔案,如果要在子目錄中搜索則需要使用searchPaths配置。例如:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/spring-cloud-samples/config-repo
7           searchPaths: foo,bar*

在上面的例子中,伺服器將會在頂級目錄和foo子目錄還有以bar開頭的子目錄中搜索配置檔案。

預設情況下,伺服器會在第一次收到配置請求時,克隆遠端倉庫。也可以在服務啟動時克隆遠端倉庫,如下:

 1 spring:
 2   cloud:
 3     config:
 4       server:
 5         git:
 6           uri: https://git/common/config-repo.git
 7           repos:
 8             team-a:
 9                 pattern: team-a-*
10                 cloneOnStart: true
11                 uri: http://git/team-a/config-repo.git
12             team-b:
13                 pattern: team-b-*
14                 cloneOnStart: false
15                 uri: http://git/team-b/config-repo.git
16             team-c:
17                 pattern: team-c-*
18                 uri: http://git/team-a/config-repo.git

在上面的例子中只有team-a的遠端倉庫會在啟動時克隆,其他的都是在第一次接受到配置請求時才克隆。

Authentication(使用者驗證)

新增username和password屬性來使用遠端倉庫基於HTTP的驗證。例如:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/spring-cloud-samples/config-repo
7           username: trolley
8           password: strongpassword

如果不使用HTTPS和使用者憑證,也可以使用SHH,在預設目錄下儲存key(~/.ssh),並且URI指向一個SSH地址(例如:[email protected]:configuration/cloud-configuration)。

Git SSH configuration using properties(使用屬性配置GIT SSH)

SSH配置可以使用java屬性來解決,spring.cloud.config.server.git.ignoreLocalSshSettings值必須設定為true。例如:

 1 spring:
 2     cloud:
 3       config:
 4         server:
 5           git:
 6             uri: [email protected]:team/repo1.git
 7             ignoreLocalSshSettings: true
 8             hostKey: someHostKey
 9             hostKeyAlgorithm: ssh-rsa
10             privateKey: |
11                          -----BEGIN RSA PRIVATE KEY-----
12                          MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
13                          IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
14                          ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
15                          1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
16                          oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
17                          DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
18                          fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
19                          BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
20                          EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
21                          5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
22                          +AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe
23                          pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
24                          ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
25                          xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
26                          dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
27                          PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
28                          VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
29                          FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R
30                          gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
31                          VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
32                          cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
33                          KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N
34                          CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
35                          q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
36                          69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
37                          -----END RSA PRIVATE KEY-----

其中hostKeyAlgorithm必須是ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521中的一個。

Force pull in Git Repositories

如果本地copy已經失效(即遠端倉庫有改動),則Spring Cloud Config Server會強制從從遠端庫拉取最新的配置。如下:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/spring-cloud-samples/config-repo
7           force-pull: true

如果有多個遠端倉庫的話,可以使用類似下面的配置:

 1 spring:
 2   cloud:
 3     config:
 4       server:
 5         git:
 6           uri: https://git/common/config-repo.git
 7           force-pull: true
 8           repos:
 9             team-a:
10                 pattern: team-a-*
11                 uri: http://git/team-a/config-repo.git
12                 force-pull: true
13             team-b:
14                 pattern: team-b-*
15                 uri: http://git/team-b/config-repo.git
16                 force-pull: true
17             team-c:
18                 pattern: team-c-*
19                 uri: http://git/team-a/config-repo.git

force-pull屬性預設是false。

Deleting untracked branches in Git Repositories(刪除git倉庫中未跟蹤的分支)

因為Spring Cloud Config Server會克隆遠端倉庫到本地並且會一直保持這個分支知道下次重啟,所以有一種情況是遠端分支已經被刪除,但是本地副本還仍然可用。

為了保證本地倉庫分支和遠端同步,需要設定deleteUntrackedBranches屬性,它會使Spring Cloud Config Server強制刪除本地未跟蹤的分支。例如:

1 spring:
2   cloud:
3     config:
4       server:
5         git:
6           uri: https://github.com/spring-cloud-samples/config-repo
7           deleteUntrackedBranches: true

deleteUntrackedBranches預設值為false。

3. Spring Cloud Config Client(配置客戶端)

3.1 Config First Bootstrap(啟動時先讀取config server配合)

在classpath中存在Spring Cloud Config Client的任何應用的預設行為如下:當一個config客戶端啟動時,它繫結到Config Server(通過bootstrap的配置屬性spring.cloud.config.uri)然後使用遠端配置檔案初始化spring環境。

這種行為的最終結果是所有想要到 Config Server消費的客戶端應用都需要一個配置有spring.cloud.config.uri(預設為:"http://localhost:8888")的bootstrap.yml或者環境變數。

3.2 Discovery First Bootstrap(使用服務發現尋找config server)

如果你正在使用服務發現框架例如Spring Cloud Netflix 和 Eureka Service Discovery 或者 Spring Cloud Consul,你可以把Config Server 註冊到服務註冊中心。

但是在預設的“Config First”模式下,客戶端是感知不到註冊中心中的config server服務的。如果你選擇使用服務發現來定位Config Server,那麼你需要在Config server中設定spring.cloud.config.discovery.enabled=true,

預設是false。這麼做的話就需要每個客戶端應用都要有一個bootstrap.yml或者環境變數來配置服務註冊中心的內容。例如在Spring Cloud Netflix中,你需要配置Eureka server地址(eureka.client.serviceUrl.defaultZone),

這麼做的代價是在啟動時需要一個額外的網路往返來定位服務註冊中心。好處是,只要註冊中心是固定的,那麼config server就可以隨意改變座標。預設的服務ID是configserver,但是可以通過spring.cloud.config.discovery.serviceId

來重新設定(但是通常是通過spring.application.name來設定一個服務ID)。

一般服務發現實現客戶端都是支援某些元資料對映的(例如在Eureka中的eureka.instance.metadataMap)。為了讓配置客戶端能正確連線到config server需要在Config Server中配一些服務註冊相關的元資料。

如果Config Server安全策略是基於HTTP的,你可以設定一些使用者憑證像username,password。另外,如果Config Server有上下文路徑,可以使用configPath設定。如下(bootstrap.yml):

1 eureka:
2   instance:
3     ...
4     metadataMap:
5       user: osufhalskjrtl
6       password: lviuhlszvaorhvlo5847
7       configPath: /config

3.3 Config Client Fail Fast(快速失敗響應)

在某些情況下,你可能希望服務在啟動時如果連線config server失敗時立刻響應失敗而停止啟動,可以設定spring.cloud.config.fail-fast=true。

3.4 Config Client Retry(配置客戶端重試機制)

如果你覺得config server在你啟動應用是不可達是暫時的或者偶然的,你需要在一次失敗後接著重新嘗試連線。第一步,設定spring.cloud.config.fail-fast=false。

接著將spring-retry和spring-boot-starter-aop加入到classpath中。預設行為是重試6次,初始間隔為1000ms,後續間隔乘以1.1。

可以通過設定spring.cloud.config.retry.*來改變預設行為。

注意:如果要完全控制重試機制,可以通過建立一個RetryOperationsInterceptor型別id為configServerRetryInterceptor的bean。

可以使用Spring Retry中的RetryInterceptorBuilder來建立。

3.5 Locating Remote Configuration Resources(定位遠端配置資源)

配置服務提供/{name}/{profile}/{label}的屬性源,其在客戶端應用的對映為:

  • "name" = ${spring.application.name}
  • "profile" = ${spring.profiles.active}(其實是Environment.getActiveProfiles())
  • "label" = "master"

可以使用spring.cloud.config.*來覆蓋上面的預設值。

其中可以使用label屬性回滾到以前的版本。在config server的實現中,label可以是git 標籤,分支名或者commit ID。也可以是以逗號分隔的列表。在這種列表情況下,其中的元素會一個一個的被嘗試,直到有一個成功。

3.6 Specifying Multiple Urls for the Config Server(指定多個config server url)

為了保證高可用,你可能部署多個config server,在客戶端可以使用spring.cloud.config.uri,以逗號分隔指定多個URL,或者將config server的多個例項註冊到服務註冊中心,此時客戶端需要使用之前提到的Discovery-First Bootstrap

模式(spring.cloud.config.discovery.enabled=true)。

如果Config Server使用的是HTTPS協議,則可以將使用者憑證資訊填寫在每個config server 的 url上。如果使用其他的安全機制,則不支援為每個config server配置驗證資訊。

3.7 Configuring Read Timeouts(讀取超時)

使用spring.cloud.config.request-read-timeout配置超時時間。

3.8 Security(安全機制)

如果config server使用的是HTTPS,則需要在url上指定使用者名稱和密碼,或者使用單獨的username和password屬性設定。如下(bootstrap.yml)

1 spring:
2   cloud:
3     config:
4      uri: https://user:[email protected]
1 spring:
2   cloud:
3     config:
4      uri: https://myconfig.mycompany.com
5      username: user
6      password: secret

spring.cloud.config.password 和 spring.cloud.config.username會覆蓋url中的使用者名稱和密碼。

好了,spring cloud config就先講到這。下一篇就是spring cloud netflix。如果文章中寫的有問題的希望各位讀者批評指出。如果覺得在我這學到了點東西的,特別希望您能動動可愛的小手給點個贊!^_^