Spring Cloud(15)——配置中心

Spring Cloud Config

Spring Cloud Config提供分佈式配置功能,它包含Server和Client兩部分。Server負責提供統一的配置信息,Client負責從Server獲取相應的配置信息。Server端的配置信息支持git存儲、本地文件存儲、數據庫等多種存儲方式,默認使用git存儲。

Server簡介

Spring Cloud Config Server需要添加spring-cloud-config-server依賴。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
</dependency>

然後需要在配置上添加@EnableConfigServer以啓用Spring Cloud Config Server的支持。

@SpringBootApplication
@EnableConfigServer
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

然後需要通過spring.cloud.config.server.git.uri指定git倉庫的地址,筆者下面指定的是一個本地倉庫的地址。由於Spring Cloud Config Client默認會把本地的8888端口當做Spring Cloud Config Server,所以測試時可以直接指定Spring Cloud Config Server的監聽端口是8888。

server.port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri: file:///D:/work/dev/git/projects/config_repo

在Git倉庫的根目錄下可以定義application.properties或application.yml文件,用來定義需要共享的屬性,比如我們的config_repo倉庫的根路徑下定義了application.yml文件,其內容如下,那麼Spring Cloud Config Client從Server獲取屬性info.foo的值時將得到bar。

info.foo: bar

Client簡介

Spring Cloud Config Client需要添加spring-cloud-starter-config依賴。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

添加了該依賴後Spring Cloud Config Client將進行自動配置。默認會從地址爲localhost:8888的Spring Cloud Config Server獲取配置信息。比如下面測試中,我們的屬性infoFoo的值將來自於Spring Environment的${info.foo},它會從我們本地的Spring Cloud Config Server的獲取到我們之前定義好的info.foo: bar,即獲取到bar。所以下面的單元測試是會通過的。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ClientTest {

  @Value("${info.foo}")
  private String infoFoo;

  @Test
  public void test() {
    Assert.assertEquals("bar", this.infoFoo);
  }

}

當需要的屬性值在Spring Cloud Config Server上沒有定義時,也可以從本地的資源文件中獲取。比如下面代碼中我們需要獲取屬性名爲hello的屬性值,遠程的Spring Cloud Config Server是沒有定義該屬性的。當本地的application.yml中定義了hello: world時下面的單元測試也可以通過。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ClientTest {

  @Value("${hello}")
  private String hello;

  @Test
  public void test() {
    Assert.assertEquals("world", this.hello);
  }

}

當本地和遠程同時定義了某個屬性值時遠程定義的優先級更高。比如本地的application.yml中定義了info.foo: AAA,遠程的application.yml中定義了info.foo: bar,則在本地注入${info.foo}時注入的是值bar

禁用Spring Cloud Config Client

如果某個時候你就希望使用本地的配置,而不希望被遠程的配置覆蓋,那麼你可以禁用Spring Cloud Config Client的功能,通過指定spring.cloud.config.enabled=false來禁用它。但是這個屬性不能直接加在application.yml中,因爲應用一起來Spring Cloud Config Client就生效了,就會從遠程Server和本地配置文件來獲取屬性了。爲了在Spring Cloud Config Client生效前禁用它,我們需要在bootstrap.yml或bootstrap.properties中定義spring.cloud.config.enabled=false

spring:
  cloud:
    config:
      enabled: false

指定Spring Cloud Config Server地址

Spring Cloud Config Client默認會連接http://localhost:8888的Server,所以如果你的Server的部署地址不是http://localhost:8888,則可以通過spring.cloud.config.uri進行指定。它也必須是定義在bootstrap.yml文件中才會生效的。

spring.cloud.config.uri: http://localhost:8080

分Profile管理配置信息

我們知道Spring的配置文件是可以分Profile的,同樣一個屬性的值在不同的Profile下可以有不同的值,比如生產環境下它的值可以是A,測試環境下可能就是B,比較常見的就是一些IP類的地址,比如數據庫連接信息。默認的application.properties文件是會加載的,當啓用了名爲prod的Profile時還會加載application-prod.properties文件。當在application.properties中定義了a=1,在application-prod.properties中定義了a=2時,如果啓用了名爲prod的Profile,那麼在獲取屬性a時取到的將是2。使用Spring Cloud Config的時候也可以分Profile定義多個屬性文件,默認的用application.properties或application.yml文件定義,特性的用application-<profile>.propertiesapplication-<profile>.yml定義,這些屬性都是定義在Spring Cloud Config Server側的,然後在Spring Cloud Config Client的bootstrap.yml中通過spring.profiles.active來指定需要啓用的Profile。

spring:
  profiles:
    active: prod

如果需要同時啓用多個Profile就用逗號分隔,比如下面就同時啓用了profile1和profile2。

spring:
  profiles:
    active: profile1,profile2

當同時啓用了多個Profile時,最後一個Profile的優先級更高。

如果在Client端使用的Profile是profile1,但是在從Server端取屬性配置時希望以profile2獲取,然而Client端的Profile又不能改爲profile2,此時可以通過在bootstrap.yml中的spring.cloud.config.profile屬性來指定從Server端獲取屬性配置時需要使用的Profile,即從Server端獲取屬性配置時spring.cloud.config.profile的值優先級高於spring.profiles.active

spring:
  cloud:
    config:
      profile: profile2

取應用特定的配置

在Client端可以通過spring.application.name指定應用的名稱,當指定了spring.application.name時,Client還會從Server加載應用名稱對應的yml或properties文件。比如spring.application.name=project1,則還會從Server獲取定義在project1.yml文件中的屬性,它的優先級比application.yml中定義的更高。它也可以和Profile一起使用,如當指定了激活的Profile是prod時,它還會從project1-prod.yml文件獲取屬性。

如果Client指定了spring.application.name=app,但是你又不希望它從Server端獲取屬性配置時按照app去獲取,而更希望它按照app2去獲取,但是你又不能動spring.application.name的值,此時則可以通過spring.cloud.config.name=app2來指定去Server端取屬性配置時傳遞的應用的名稱是app2。

Client配置從Server獲取數據的超時時間

Client從Server獲取數據是通過Http獲取的,默認的超時時間是3分鐘多5秒,如果你覺得這個時間太長或者太短,可以通過spring.cloud.config.requestReadTimeout進行調整,單位是毫秒。

spring:
  cloud:
    config:
      request-read-timeout: 10000

指定Client快速失敗

正常情況下Spring Cloud Config Client在連接不上Spring Cloud Config Server時不會報錯,它只是會取不到遠程配置的屬性而已。如果你希望Spring Cloud Config Client在啓動時如果連接不上Spring Cloud Config Server就報錯,啓動失敗,則可以在Client端的bootstrap.yml中配置spring.cloud.config.failFast=true

spring:
  cloud:
    config:
      fail-fast: true

Client重試

可能會有偶爾的網絡波動導致連接不上Spring Cloud Config Server,爲了避免這種情況,我們通常可以在Client端加入Spring Retry依賴,這樣可以使用它的Retry機制。

<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
</dependency>

Spring Retry默認會最多重試6次,初始間隔1秒,以1.1倍遞增。如果有需要可以在bootstrap.yml中進行如下配置。下面的配置定義了初始延時時間是2秒,每次以1.2倍的速度增長,最大重試間隔時間是5秒,最多重試10次。

spring:
  cloud:
    config:
      retry:
        initial-interval: 2000
        max-attempts: 10
        max-interval: 5000
        multiplier: 1.2

使用遠程Git

如果希望使用遠程Git倉庫地址也是可以的,只需指定對應的遠程倉庫地址。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git

可以通過spring.cloud.config.server.git.skipSslValidation=true來忽略SSL證書校驗,默認是false。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          skip-ssl-validation: true

每個應用一個Git倉庫

Server端在指定Git倉庫時可以使用一個特殊的佔位符{application},比如下面這樣。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/{application}

{application}佔位符的值將由Client端的spring.application.name來決定,比如客戶端指定的spring.application.name的值爲abc,則該Client對應的Server的遠程倉庫地址就是https://github.com/elim168/abc,即該Client會從https://github.com/elim168/abc這個Git倉庫的application.yml等文件中獲取屬性。

每個Profile一個倉庫

Server端的Git倉庫地址還可以使用的一個特殊佔位符是{profile},它的值由Client端啓用的Profile決定。通過它可以實現每個Profile一個倉庫,比如Server端配置Git倉庫地址爲https://github.com/elim168/{profile},當在Client端的bootstrap.yml文件中指定spring.profiles.activespring.cloud.config.profile的值爲abc時,對應的Server端的Git倉庫地址就是https://github.com/elim168/abc

每個組織一個Git倉庫

Server端在指定Git倉庫時可以使用一個特殊的佔位符{application},比如下面這樣。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/{application}

{application}佔位符的值將由Client端的spring.application.name來決定,此時Client端的spring.application.name的值需是organation(_)application的格式,比如客戶端指定的spring.application.name的值爲abc(_)def,則該Client對應的Server的遠程倉庫地址就是https://github.com/abc/def。這樣就實現了根據組織和應用來決定Git倉庫地址。

配置多倉庫

Server可以同時配置多個Git倉庫地址,然後通過application和profile來動態選擇Git倉庫,比如下面配置通過spring.cloud.config.server.git.uri指定了默認的Git倉庫地址是https://github.com/elim168/config_repo,通過spring.cloud.config.server.git.repos定義了多個Git倉庫,app1、app2、app3是倉庫的命名,每個倉庫可以通過pattern屬性來指定匹配條件,它是application/profile的組合,其中profile可以省略,沒有指定profile時表示匹配所有的Profile。沒有指定pattern時,pattern默認取倉庫的名稱,比如下面的app1。當Client端指定的spring.application.nameapp1時,Server端將從https://github.com/elim168/app1這個Git倉庫獲取屬性值;當Client端指定的spring.application.name的值是以app2開頭的,比如app2、app2019等時,Server端將從https://github.com/elim168/app2這個Git倉庫獲取屬性值;當Client指定的spring.application.name的值是以app3開頭的,且指定的Profile是以dev開頭的時Server端將從https://github.com/elim168/app3這個Git倉庫獲取屬性。其它情況將從默認的https://github.com/elim168/config_repo這個Git倉庫獲取屬性值。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo
          repos:
            app1: https://github.com/elim168/app1
            app2:
              patten: app2*
              uri: https://github.com/elim168/app2
            app3:
              pattern: app3/dev*,app3*/dev*
              uri: https://github.com/elim168/app3

此種情況配置Git倉庫地址時也可以使用{application}{profile}等佔位符。

搜索Git倉庫子目錄

默認情況下Spring Cloud Config Server在會指定Git倉庫的根路徑尋找對應的application.yml文件。可以通過指定searchPaths來指定需要搜索的子目錄。比如下面的配置除了在根路徑尋找配置外,還會在app1子目錄下尋找配置文件,當根路徑和指定的子路徑下都存在某個配置屬性時,子路徑下指定的優先級更高。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          search-paths: app1

searchPaths也是可以同時指定多個的,對應一個數組,也可以使用通配符。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          search-paths: app1,app2/*

searchPaths中也可以使用佔位符,比如{application},這樣也可以實現一個Git倉庫同時管理多個應用的配置文件。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          search-paths: '{application}'

指定使用的Git分支

Spring Cloud Config Server在使用Git倉庫作爲資源文件的來源時,默認會獲取指定的Git倉庫的master分支。可以在Server端通過spring.cloud.config.server.defaultLabel指定默認的Git分支,它對所有的Client都生效。

spring:
  cloud:
    config:
      server:
        default-label: 'dev'

如果只是某個Client需要應用特殊的Git分支,則可以在Client端的bootstrap.yml文件中通過spring.cloud.config.label指定。比如下面就指定了本Client需要使用Server端的Git倉庫的dev分支。

spring:
  cloud:
    config:
      label: dev

Spring Cloud Config Server端也可以在可以使用佔位符的位置使用{label}佔位符,對應的取值來自客戶端的spring.cloud.config.label。當服務端使用的是Git存儲時,這個佔位符通常用處不大,但是當服務端使用的是其它存儲,比如本地文件存儲時就還是有用的,因爲可以通過它來區別二級目錄。

關於遠程倉庫的克隆

默認情況下,Spring Cloud Config Server會在第一次請求獲取配置信息時克隆對應的遠程Git倉庫到本地,可以通過其cloneOnStartup屬性來指定在Server啓動後就克隆。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          clone-on-start: true

當本地倉庫的某些文件被污染了以後,Server在從遠程倉庫獲取更新時將會失敗,可以通過指定forcePull屬性爲true來強制更新,此時將忽略本地倉庫的變更,強制使用遠程倉庫的內容覆蓋本地倉庫。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          search-paths: '{application}'
          clone-on-start: true
          force-pull: true

克隆的遠程倉庫默認會存放在系統臨時目錄下,可以通過basedir屬性指定克隆的遠程倉庫在本地存儲的路徑。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          basedir: /git-repo

Spring Cloud Config Server的本地倉庫默認會在每次有請求需要獲取配置信息時都從遠程倉庫同步一次最新的配置。我們可以通過refreshRate屬性來配置本地倉庫從遠程倉庫同步數據的時間間隔,單位是秒。比如下面就定義了每60秒從遠程倉庫同步一次。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/elim168/config_repo.git
          refresh-rate: 60

Server使用本地配置文件

之前介紹的都是Spring Cloud Config Server使用Git倉庫作爲配置文件的來源,不管是遠程倉庫還是本地倉庫。Spring Cloud Config Server還支持本地的非Git倉庫作爲配置文件的來源。使用本地配置文件時需要指定Server端的spring.profiles.active=native,然後通過spring.cloud.config.server.native.searchLocations指定本地配置文件存儲的位置,默認會讀取根路徑和config子目錄下的配置文件。比如下面的配置就定義了Spring Cloud Config Server將使用本地的D:/tmp/config目錄下的application.properties、application.yml等文件作爲屬性來源的配置文件,也可以是D:/tmp/config/config目錄下的。如果指定了激活的Profile,也會獲取application-<profile>.<properties|yml>文件。

spring:
  cloud:
    config:
      server:
        native:
          search-locations: D:/tmp/config
  profiles:
    active: native

spring.cloud.config.server.native.searchLocations的值也可以使用{application}{profile}等佔位符,比如下面這樣。

spring:
  cloud:
    config:
      server:
        native:
          search-locations: D:/tmp/config/{application}

spring.cloud.config.server.native.searchLocations也支持同時指定多個路徑,它是一個數組。

如果需要讀取Spring Cloud Config Server應用本地Classpath下的內容,則可以直接配置spring.cloud.config.server.native.searchLocations的值爲classpath:/xxx,比如配置都存放在應用Classpath下的configs目錄,則可以配置spring.cloud.config.server.native.searchLocations=classpath:/configs。因爲它不支持讀取標準的Classpath下的內容(如classpath:/classpath:/config/等),所以不要指定searchLocations爲Spring Cloud Config Server標準的讀取配置的路徑,如classpath:/classpath:/config/等。如果要使用searchLocations爲classpath:/config/則可以指定Spring Cloud Config Server自身的讀取配置文件的位置不包含classpath:/config/,比如只包含classpath:/。Spring Boot的配置文件路徑默認是可以通過spring.config.location參數進行定義的,通過該參數可以在部署時把配置文件定義在jar包以外的文件系統,然後通過Java啓動參數-Dspring.config.location來指定配置文件在文件系統的具體位置或目錄。但是對於Spring Cloud Config而言,項目特定的配置如果我們是配置在Classpath下面的,想要在部署時定義在jar包以外可以通過-Dspring.cloud.config.server.native.searchLocations指定,也可以定義一個額外的參數,比如是appConfig.location,它的默認值可以是classpath:/configs,然後可以把該參數賦值給spring.cloud.config.server.native.searchLocations,然後通過Java啓動參數來指定appConfig.location的值。

Server使用數據庫存儲

Spring Cloud Config Server也支持使用數據庫作爲存儲。使用數據庫作爲存儲時需要指定激活的Profile爲jdbc,需要引入spring-jdbc依賴,相應的JDBC驅動。筆者使用的是MySQL,所以添加如下依賴。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

然後需要基於Spring Boot自動配置機制配置數據源連接信息。

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false
    username: root
    password: elim

使用基於數據庫的Spring Cloud Config Server時,其默認會通過SELECT KEY, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?語句獲取屬性信息,所以我們需要在所使用的數據庫中創建一個名爲PROPERTIES的表,至少擁有APPLICATION、PROFILE、LABEL、KEY和VALUE列,類型一般都是字符串型。我們也可以通過spring.cloud.config.server.jdbc.sql屬性來調整這個SQL,這樣可以有不同的表名稱,表結構等。筆者使用的MySQL數據庫key是一個關鍵字,不能直接用來作爲列名稱,需要加上``號,所以筆者需要重寫其SQL。

spring:
  cloud:
    config:
      server:
        jdbc:
          sql: 'SELECT `KEY`, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?'

我們可以看到默認的SQL語句需要接收3個參數,分別是應用名稱,激活的Profile和LABEL,客戶端不指定LABEL時,服務端默認使用master,如有需要可以在客戶端通過spring.cloud.config.label來指定使用的LABEL。所以假設我們客戶端的應用名是app1,激活的profile是dev,沒有指定LABEL,需要獲取KEY爲info.foo的值,那麼默認會尋找APPLICATION=app1,PROFILE=dev,LABEL=master,KEY=info.foo那一行的VALUE。那麼你可能會想,它有沒有像前面介紹過的幾種存儲一樣,有獲取默認屬性值的機制呢?答案是有的,當指定APPLICATION的行記錄不存在時,會獲取APPLICATION=application的行記錄,當指定PROFILE的行記錄不存在時會獲取PROFILE=default的行記錄。所以假設我們的PROPERTIES表數據如下表所示,當客戶端的應用名稱是app1、激活的PROFILE是dev時,獲取到的屬性info.foo的值是value1;當變更PROFILE爲test時,獲取到的值是value2;當變更PROFILE爲prod時,獲取到的值是什麼呢?此時獲取到的會是value0。如果客戶端的應用名稱是app2,激活的PROFILE是dev,那麼它獲取到的屬性info.foo的值是value3;如果它激活的PROFILE是test,那麼它獲取到的屬性值就是value4了。

APPLICATION PROFILE LABEL KEY VALUE
app1 default master info.foo value0
app1 dev master info.foo value1
app1 test master info.foo value2
application dev master info.foo value3
application default master info.foo value4

基於JDBC的存儲,其底層不是每次從數據庫中取特定的Key,如你所看到的前面介紹的它獲取屬性的SQL那樣,它是一次獲取滿足條件的所有的屬性鍵值對。

同時使用多種屬性來源

方式一

當需要同時使用多種屬性來源時,需要指定Server端激活的Profile爲composite。然後通過spring.cloud.config.server.composite指定每個屬性來源的配置,每個配置需要通過type屬性指定屬性來源類型,然後可以指定特定屬性來源類型的其它屬性,比如Git可以通過uri指定Git倉庫地址,jdbc通過sql指定查詢的SQL語句等。下面的配置就同時組合了3種屬性來源,第一個是遠程Git倉庫,第二個是本地Git倉庫,第三個是來源於數據庫。當定義了多種屬性來源時,按照從上到下的定義順序,越是定義在上面的優先級越高,且每種屬性來源是解析一個屬性值時是獨立的,每個屬性來源分別解析好屬性值後,再按照從上到下的優先級順序進行彙總。也就是說如果在第一個屬性來源的application.properties中定義了info.foo=value1,再第二個屬性來源中的application-dev.properties中定義了info.foo=value2,即使當前Client的激活Profile是dev,其獲取到的info.foo屬性的值也是value1,而不是value2

spring:
  profiles:
    active: composite
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false
    username: root
    password: elim
spring.cloud.config.server.composite:
  -
    type: git
    uri: https://github.com/elim168/config_repo.git
  -
    type: git
    uri: file:///D:/work/dev/git/projects/config_repo
  -
    type: jdbc
    sql: 'SELECT `KEY`, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?'

方式二

直接指定激活的Profile爲需要的屬性來源的類型的名稱,git、native、jdbc等。下面代碼就通過spring.profiles.active指定了兩種屬性來源,git和jdbc。然後還通過下級屬性order指定了它們的優先級,數字越小的優先級越高,所以在下面配置中Git倉庫中配置的屬性的優先級比通過jdbc獲取到的更高。方式二與方式一相比,它的缺點是不能同時指定兩個同類型的屬性來源。

spring:
  profiles:
    active: git,jdbc
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useSSL=false
    username: root
    password: elim
  cloud:
    config:
      server:
        jdbc:
          sql: 'SELECT `KEY`, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?'
          order: 10
        git:
          uri: file:///D:/work/dev/git/projects/config_repo
          order: 8

Server端強制Client端共享某個屬性

Server端可以配置某個屬性讓所有的客戶端都強制共享,而忽略它們在配置的屬性來源中配置的值,這是通過spring.cloud.config.server.overrides屬性來配置的,其下可以配置多個需要覆蓋的屬性。比如下面配置中我們配置了Server端的屬性來源是來自於一個本地的Git倉庫,如果在本地的Git倉庫的根路徑下的application.yml中配置了info.foo=abc,因爲我們通過spring.cloud.config.server.overrides配置了info.foo=override-bar,所以當Client端需要從Server端獲取屬性info.foo的值時獲取到的都將是override-bar

spring:
  cloud:
    config:
      server:
        git:
          uri: file:///D:/work/dev/git/projects/config_repo
        overrides:
          info:
            foo: override-bar

對屬性值加解密

Spring Cloud Config Server內部定義了一個org.springframework.cloud.config.server.encryption.EncryptionController,其定義了一些/encrypt/decrypt接口,可以對數據進行加解密。使用這些接口我們需要在Spring Cloud Config Server定義一個org.springframework.security.crypto.encrypt.TextEncryptor類型的bean。Spring Cloud中已經爲我們內置了一個,可以直接在bootstrap.yml文件中定義encrypt.key屬性來定義一個用來進行加密的密鑰,然後Spring Cloud就會自動創建一個TextEncryptor類型的bean供加解密使用,默認使用的是基於AES算法的實現。下面代碼中就指定了用於加解密的密鑰是AAA。

encrypt:
  key: AAA

還可以通過encrypt.salt指定一個Salt,其默認值是deadbeef

指定了密鑰後就可以通過POST方法請求Spring Cloud Config Server的/encrypt進行加密了,加密後的內容可以配置到Spring Cloud Config Server的屬性值配置中,在其內容前面加上{cipher}前綴,那麼當Spring Cloud Config Client來請求該屬性值時,Spring Cloud Config Server發現該值是包含{cipher}前綴的,會把去掉{cipher}部分的內容進行解密後再返回給客戶端。比如筆者加密了明文ABCD後的密文是14276a05d8179cec776bdf2164ca281aaef645cf30bc1b9d2f35b1e2247e5b1f,我們在Server端的屬性文件中配置了屬性hello.encrypt的值爲該加密後的值加上{cipher}前綴。

hello.encrypt: '{cipher}14276a05d8179cec776bdf2164ca281aaef645cf30bc1b9d2f35b1e2247e5b1f'

當客戶端請求屬性hello.encrypt的值時將返回ABCD

可以通過spring.cloud.config.server.prefix屬性指定Server端對外發布的接口的一個統一前綴。比如指定spring.cloud.config.server.prefix=/config,則加密接口對外的URL將從/encrypt變爲/config/encrypt。指定了該prefix後,Client端在配置Server地址時也需要加上對應的prefix。比如原來Server端的地址是http://localhost:8080,加了前綴/config後Client端配置Server地址時需要改成http://localhost:8080/config

使用非對稱加密

Spring Cloud Config Server也支持非對稱加密。非對稱加密需要一個KeyStore,應用如下KeyStore命令將在當前目錄下生成一個使用RSA算法,別名爲testkey,密鑰密碼爲key123456,KeyStore密碼爲123456的KeyStore文件——server.jks文件。

keytool -genkeypair -alias testkey -keyalg RSA -dname "CN=Server1,OU=Unit,O=Elim,L=ShenZhen,S=GD,C=CN" -keypass key123456 -keystore server.jks -storepass 123456

然後把生成的server.jks文件放到Spring Cloud Config Server的Classpath下。筆者放的是Classpath根路徑,所以在bootstrap.yml中進行了如下配置。這樣Spring Cloud Config Server使用的加密算法就變爲了非對稱加密算法RSA。

encrypt:
  key-store:
    location: server.jks
    alias: testkey
    password: 123456
    secret: key123456

同樣請求Server端的/encrypt加密ABCD得到的加密內容如下:

AQCcXO55WlXLEC/ZRwCENdWi2c6kqD6qqBdFO9pwXuGmR/AS3FrI8+UopZ7rfVGDev3bUDb5V4/DacTz4b+eQ7Gs3VerBg0RS6DPbK3Ypbtajjep3k3S4V1hqtSrDkEkRsT33kKbMHRiZKhwvjV+P1mIsNVwjxFpJl6qhFks9gV/oByllrHmowuAw+GS+cu2ishponaEVUJGfn+UpFQuxA/9l/ia585/4rTKLqlhzpjwDFj5o9t9iesmZ+IaoylrK7DGqK31+tO4wrv8xtpmQlCox+ykc7BbJdSh6nXeR3jC1bEJ7M8F6G1mXMRu2mKCH36Nn+MenGnUFSD7SfanIDgp/6cMmK2HfXaSr5ao+ibwyLnb1Tmjs0szn2d0tSrgWBE=

Server端使用Http Basic認證

爲了安全考慮,我們的Server端可以加入認證機制,最簡單的就是Http Basic認證。Spring Boot Security提供了開箱即用的Http Basic認證機制,我們可以在Server端的pom.xml中添加如下Spring Security的依賴。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

然後可以在Server端的application.yml中添加如下配置用於指定可通過認證的用戶名和密碼。下面的配置指定了用戶名是user,密碼是password。

spring:
  security:
    user:
      name: user
      password: password

然後在Spring Cloud Config Client端需要配置用於Http Basic認證的用戶名和密碼。這需要配置在其bootstrap.yml文件中。

spring:
  cloud:
    config:
      uri: http://localhost:8080
      username: user
      password: password

整合使用服務發現

Spring Cloud Config還可以與Spring Cloud的服務發現機制一起使用。筆者將採用基於Eureka的實現進行介紹。當與Eureka一起使用時,那首先當然是在Spring Cloud Config Server應用中添加Eureka的依賴。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然後Spring Cloud Config Server應用作爲Eureka的Client,需要配置Eureka Client的一些相關信息,這些信息是配置在application.yml中的。比如下面這樣。

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.hostname}:${server.port}
  client:
    service-url:
      defaultZone: http://localhost:8089/eureka/

還需要指定spring.application.name的值,它將作爲Eureka註冊的服務名,建議指定爲configserver,因爲它默認是Spring Cloud Config Client使用Spring Cloud服務發現機制時對應Spring Cloud Config Server的服務名。

Spring Cloud Config Client也需要是一個Eureka Client,所以它也需要添加Eureka Client的依賴。它的Eureka Client的相關配置信息是配置在bootstrap.yml中的。與Spring Cloud Config Server相比,它不需要作爲一個服務註冊到Eureka Server,所以需要指定eureka.client.registerWithEureka=false,然後不需要指定Eureka的Instance相關信息。

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8089/eureka/
    register-with-eureka: false

Spring Cloud Config Client如果需要使用服務發現機制來從Server端獲取配置信息,需要指定spring.cloud.config.discovery.enabled=true。那麼它默認就會從Eureka Server獲取服務名爲configserver的一個實例的信息,進而再訪問該Spring Cloud Config Server實例獲取配置的屬性信息。如果Spring Cloud Config Server註冊到Eureka Server的名稱不是configserver,則可以通過spring.cloud.config.discovery.serviceId進行指定,比如下面就指定了Spring Cloud Config Server對應的serviceId是configserver1

spring:
  cloud:
    config:
      discovery:
        enabled: true
        service-id: configserver1

如果我們的Spring Cloud Config Server是需要進行Http Basic認證的,則我們可以通過eureka.instance.metadataMap來指定相應的用戶名和密碼,比如下面這樣。

eureka:
  instance:
    metadata-map:
      user: user
      password: password

如果Spring Cloud Config Server是指定了spring.cloud.config.server.prefix的,則在Client端也可以通過eureka.instance.metadataMap來指定相應的前綴,這對應configPath屬性,比如下面這樣。

eureka:
  instance:
    metadata-map:
      configPath: /config

參考文檔

(注:本文是基於Spring Cloud Finchley.SR1所寫)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章