1. 程式人生 > >Spring Cloud第十篇 | 分散式配置中心Config

Spring Cloud第十篇 | 分散式配置中心Config

本文是Spring Cloud專欄的第十篇文章,瞭解前九篇文章內容有助於更好的理解本文:

  1. Spring Cloud第一篇 | Spring Cloud前言及其常用元件介紹概覽

  2. Spring Cloud第二篇 | 使用並認識Eureka註冊中心

  3. Spring Cloud第三篇 | 搭建高可用Eureka註冊中心

  4. Spring Cloud第四篇 | 客戶端負載均衡Ribbon

  5. Spring Cloud第五篇 | 服務熔斷Hystrix

  6. Spring Cloud第六篇 | Hystrix儀表盤監控Hystrix Dashboard

  7. Spring Cloud第七篇 | 宣告式服務呼叫Feign

  8. Spring Cloud第八篇 | Hystrix叢集監控Turbin

  9. Spring Cloud第九篇 | 分散式服務跟蹤Sleuth

一、介紹

    Spring Cloud Config是Spring Cloud團隊建立的一個全新專案,用來為分散式系統中的基礎設施和微服務應用提供集中化的外部配置支援,它分為服務端(config server)與客戶端(config client)兩個部分。其中服務端也稱為分散式配置中心,它是一個獨立的微服務應用,用來連線配置倉庫併為客戶端提供獲取配置資訊、加密/解密資訊等訪問介面,而客戶端則是微服務架構中的各個微服務應用或基礎設施,它們通過指定的配置中心來管理應用資源與業務相關的配置內容, 並在啟動的時候從配置中心獲取和載入配置資訊。Spring Cloud Config實現了對服務端和客戶端中環境變數和屬性配置的抽象對映,所以它除了適用於Spring構建的應用程式之外, 也可以在任何其他語言執行的應用程式中使用。由於Spring Cloud Config實現的配置中心預設採用Git來儲存配置資訊,所以使用Spring Cloud Config構建的配置伺服器,天然就支援對微服務應用配置資訊的版本管理,並且可以通過Git客戶端工具來方便地管理和訪問配置內容。當然它也提供了對其他儲存方式的支援,比如SVN倉庫、本地化檔案系統。 

二、構建Config Server

1、新增依賴

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

2、在啟動類上添加註解@EnableConfigServer

3、在application.yml檔案中新增配置

spring:
  application:
    name: springcloud-config-server
  cloud:
    config:
      server:
        git:
          #配置git倉庫地址
          uri: https://gitee.com/coding-farmer/config-center
          #配置倉庫路徑
          search-paths: "{profile}"
          #訪問git倉庫的使用者名稱
          username:
          #訪問git倉庫的密碼
          password:
          #配置中心通過git從遠端git庫,有時本地的拷貝被汙染,
          #這時配置中心無法從遠端庫更新本地配置,設定force-pull=true,則強制從遠端庫中更新本地庫
          force-pull: true
          #預設從git倉庫克隆下載的在C:/Users/<當前使用者>/AppData/Local/Temp
          #basedir:

server:
  port: 8888

    如果Git倉庫為公開倉庫,可以不填寫使用者名稱和密碼,如果是私有倉庫需要填寫,本案例使用的是碼雲公開倉庫。

4、配置中心倉庫目錄結構為:按環境拆分

    遠端碼雲倉庫https://gitee.com/coding-farmer/config-center中有3個資料夾,分別是dev,prod,test,裡面存放著相應環境的配置檔案

5、測試返回資料

http請求地址和資原始檔對映如下:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

    此時啟動我們的配置中心,通過/{application}/{profile}/[{label}]就能訪問到我們的配置檔案了,其中application表示配置檔案的名字,對應我們上面的配置檔案就是config;profile表示環境,我們有dev、test、prod還有預設,label表示分支,預設我們都是放在master分支上

訪問:http://localhost:8888/configclient/dev,預設分支為master分支

如下圖則證明配置服務中心可以從遠端程式獲取配置資訊:

    從瀏覽器上可以看到我們放在倉庫中的配置檔案資訊。JSON中的name表示配置檔名application的部分,profiles表示環境部分,label表示分支,多了一個version,實際上就是我們碼雲上提交資訊時產生的版本號,當我們訪問成功後,我們還可以看到控制檯列印瞭如下日誌:

INFO 6600 --- [nio-8888-exec-1] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5c4fca8a: startup date [Thu Aug 15 15:12:07 CST 2019]; root of context hierarchy
INFO 6600 --- [nio-8888-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository  : Adding property source: file:/C:/Users/Administrator/AppData/Local/Temp/config-repo-6372945341655107732/dev/configclient-dev.yml
INFO 6600 --- [nio-8888-exec-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5c4fca8a: startup date [Thu Aug 15 15:12:07 CST 2019]; root of context hierarchy

    實際上是配置中心通過git clone命令將配置檔案在本地儲存了一份,這樣可以確保在git倉庫掛掉的時候我們的應用還可以繼續執行,當微服務A/B嘗試去從Config Server中載入配置資訊的時候,Config Server會先通過git clone命令克隆一份配置檔案儲存到本地,此時我們斷掉網路,再訪問http://localhost:8888/configclient/dev一樣還可以拿到資料,此時的資料就是從本地獲取的。

    如果有兩個字首名相同檔案,例如一個configclient.yml,一個configclient-dev.yml。那麼在訪問相同字首的檔案時,config-server會對這兩個檔案進行一個合併。例如configclient.yml有一段配置是configclient-dev.yml沒有的,理應訪問configclient-dev.yml的時候是沒有那段配置的,但最終的訪問的結果卻是它倆合併之後的內容,即configclient-dev.yml會擁有configclient.yml裡所配置的內容。

    到此config server構建完成。

三、構建Config Client

    Config Client也就是你的微服務應用例如(springcloud-service-consumer、springcloud-service-feign、springcloud-service-provider等等,這些模組相對於Config Server來說都是Config Client),但是為了保持其他案例模組的純潔乾淨,這裡就單獨構建一個Config Client命名為:springcloud-config-client

1、引入依賴

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

2、新建bootstrap.yml

    不瞭解bootstrap.yml可以參考,SpringCloud入門之常用的配置檔案application.yml和 bootstrap.yml區別:https://www.cnblogs.com/BlogNetSpace/p/8469033.html

spring:
  application:
    name: springcloud-config-client
  cloud:
    config:
      #uri則表示配置中心的地址
      uri: http://localhost:8888
      #注:config 客戶端在沒有 spring.cloud.config.name屬性的時候,服務端{application} 獲取的是客戶端
      #spring.application.name的值,否則,獲取的是 spring.cloud.config.name的值。
      #1)、當沒有spring.cloud.config.name時,客戶端獲取的是spring.application.name 所對應的git庫中的檔案,並且只能獲取一個檔案
      #2)、當一個專案中有需求要獲取多個檔案時,就需要用到spring.cloud.config.name這個屬性,以逗號分割
      name: configclient
      profile: dev
      #label對應了label部分
      label: master
server:
  port: 8881

3、編寫Controller

@RestController
public class MyController {

    //配置中心裡面配置的env屬性
    @Value("${env}")
    private String env;

    @RequestMapping("/index")
    public String env(){
        return env;
    }
}

4、訪問http://localhost:8881/index

四、安全保護

    開發環境中我們的配置中心肯定是不能隨隨便便被人訪問的,我們可以加上適當的保護機制,由於微服務是構建在Spring Boot之上,所以整合Spring Security是最方便的方式。

1、新增依賴

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

2、預設情況下,我們可以獲得一個名為user的使用者,並且在配置中心啟動的時候,在日誌中打印出該使用者的隨機密碼

Using generated security password: 9ca37d4c-c786-44be-8919-1825a0baaadb

    大多數情況下,我們並不會使用隨機生成密碼的機制,我們可以在配置中心服務端application.yml檔案中指定使用者名稱密碼

spring:
  security:
    user:
      name: coding-farmer
      password: 123456

    當再次訪問http://localhost:8888/configclient/dev配置中心服務端的時候需要輸入密碼,顯示如下圖

3、由於我們已經為config server設定了安全保護,如果我們這個時候連線到配置中心的客戶端中沒有設定響應對應的安全資訊,在獲取配置資訊時會返回401錯誤,所以需要通過配置的方式在客戶端中加入安全資訊來通過校驗,比如:

spring:
  cloud:
    config:
      username: coding-farmer
      password: 123456

五、加密解密

1、對稱加密

    在微服務架構中,我們通常會採用DevOps的組織方式來降低因團隊間溝通造成的巨大成本,以加速微服務應用的交付能力。這就使得原本由運維團隊控制的線上資訊將交由微服務所屬組織的成員自行維護,其中將會包括大量的敏感資訊,比如資料庫的賬戶與密碼等。顯然,如果我們直接將敏感資訊以明文的方式儲存於微服務應用的配置檔案中是非常危險的。針對這個問題, Spring Cloud Config提供了對屬性進行加密解密的功能,以保護配置檔案中的資訊保安。比如下面的例子

spring.datasource.username=root
spring.datasource.password={cipher}22fedb745505ffcd1dec962bee3c1f0f3af8a3e6b6930eee9afb8659b16a0c630fd256a181319704b806df90f38e7371

    在Spring Cloud Config中通過在屬性值前使用{cipher)字首來標註該內容是一個加密值,當微服務客戶端載入配置時,配置中心會自動為帶有{cipher}字首的值進行解密。通過該機制的實現,運維團隊就可以放心地將線上資訊的加密資源給到微服務團隊,而不用擔心這些敏感資訊遭到洩漏的風險。

1-1、使用前提

    在使用 Spring Cloud Config的加密解密功能時,有一個必要的前提需要我們注意。為了啟用該功能,我們需要在配置中心的執行環境中安裝不限長度的JCE版本(Unlimited Strength Java Cryptography Extension)。雖然,JCE功能在JRE中自帶,但是預設使用的是有長度限制的版本。我們可以從 Oracle的官方網站下載到它,它是一個壓縮包,解壓後可以看到下面三個檔案:

    我們需要將local_policy.jar和US_export_policy.jar兩個檔案複製到%JAVA_HOME%\jre\lib\security目錄下,覆蓋原來的預設內容。注意:我這裡使用的是JDK8,應該下載對應版本的JCE

1-2、相關端點

    在完成了JCE的安裝後,可以嘗試啟動配置中心。在控制檯中,將會輸出一些配置中心特有的端點,主要包括如下幾個

  • /encrypt/status:檢視加密功能狀態的端點。

  • /key:檢視金鑰的端點。

  • /encrypt:對請求的body內容進行加密的端點。

  • /decrypt:對請求的body內容進行解密的端點。

可以嘗試通過GET請求訪問/encrypt/status端點,我們將得到如下內容

    該返回資訊說明當前配置中心的加密功能還不能使用,因為沒有為加密服務配置對應的金鑰。

1-3、配置祕鑰

    我們可以通過encrypt.key屬性在bootstrap.yml(為什麼要寫bootstrap.yml檔案裡檢視,關於spring cloud config加密EncryptionTooWeakException異常說明:https://www.iteye.com/blog/357029540-2433259)配置檔案中直接指定金鑰資訊(對稱性金鑰),比如:

encrypt:
  key: coding-farmer

加入上述配置資訊後,重啟配置中心,再訪問/encrypt/status端點,我們將得到 如下內容:

    此時,我們配置中心的加密解密功能就已經可以使用了,不妨嘗試訪問一下/encrypt 和/decrypt端點來使用加密和解密的功能。注意,這兩個端點都是POST請求,加密和解密資訊需要通過請求體來發送。

1-4、使用postman進行加密:http://localhost:8888/encrypt

 

1-5、使用postman解密:http://localhost:8888/decrypt

 

1-6、把加密的內容複製到你的配置檔案中,提交到倉庫即可

    如下圖,加密內容前面要有{cipher}開頭表示該值是一個加密字元,配置中心config-server在獲取到這個值之後會先對值進行解密,解密之後才會返回給客戶端使用,如果是yml檔案key=value,value值要加單引號

1-7、訪問:http://localhost:8888/configclient/dev,顯示如下則加密成功

2、非對稱加密

    Spring Cloud Config的配置中心不僅可以使用對稱性加密,也可以使用非對稱性加密 (比如RSA金鑰對)。雖然非對稱性加密的金鑰生成與配置相對複雜一些,但是它具有更高的安全性。下面,我們來具體介紹一下如何使用非對稱加密

    首先,需要通過keytool工具來生成金鑰對。keytool是JDK中的一個金鑰和證書管理工具。它使使用者能夠管理自己的公鑰/私鑰對及相關證書,用於(通過數字簽名)自我認證(使用者向其他使用者/服務認證自己)或資料完整性以及認證服務。在JDK1.4以後的版本中都包含了這一工具,它的位置在%JAVA_HOME%\bin\keytool.exe。

    生成金鑰的具體命令如下所示,在DOS視窗輸入:

keytool -genkeypair -alias config-server -keyalg RSA -keystore config-server.keystore
C:\Program Files\Java\jdk1.8.0_101\bin>keytool -genkeypair -alias config-server -keyalg RSA -keystore config-server.keystore
輸入金鑰庫口令: 123456
再次輸入新口令: 123456
您的名字與姓氏是什麼?
  [Unknown]:  coding-farmer
您的組織單位名稱是什麼?
  [Unknown]:  company
您的組織名稱是什麼?
  [Unknown]:  organization
您所在的城市或區域名稱是什麼?
  [Unknown]:  city
您所在的省/市/自治區名稱是什麼?
  [Unknown]:  province
該單位的雙字母國家/地區程式碼是什麼?
  [Unknown]:  china
CN=coding-farmer, OU=company, O=organization, L=city, ST=province, C=china是否正確?
  [否]:  y

輸入 <config-server> 的金鑰口令
        (如果和金鑰庫口令相同, 按回車): 456789
再次輸入新口令: 456789

    另外,如果不想逐步輸入那些提示資訊,可以使用-dname來直接指定,而祕鑰庫口令可使用-storepass和-keypass來直接指定。所以,我們可以通過下面命令直接建立與上述命令一樣的祕鑰庫。

keytool -genkeypair -alias config-server -keyalg RSA \
 -dname "CN=coding-farmer, OU=company, O=organization, L=city, ST=province,C=china" \
 -storepass 123456 \
 -keystore config-server.keystore \
 -keypass 456789 \

    預設情況下,使用上述命令創鍵的祕鑰只有90天有效期,如果想要調整它的有效期,可以通過增加-validity引數來實現,比如我們可以通過下面的命令,讓祕鑰的有效期延長到一年:

 keytool -genkeypair -alias config-server -keyalg RSA \
 -dname "CN=coding-farmer, OU=company, O=organization, L=city, ST=province, C=china" \
 -storepass 123456 \
 -keystore config-server.keystore \
 -keypass 456789 \
 -validity 365 \

    上述的三種命令生成方式,最終都會在命令的當前執行目錄下生成一個config-server. keystore檔案。下面,我們需要將它儲存在配置中心的檔案系統中的某個位置, 比如將該檔案拷貝到config-server的src\main\resources目錄下,然後在配置中心中加入相關的配置資訊

encrypt:
  key-store:
    location: config-server.keystore
    alias: config-server
    password: 123456
    secret: 456789

非對稱加密的配置資訊也可以通過環境變數的方式進行配置,它們對應的具體變數名如下:

ENCRYPT_KEY_STORE_LOCATION
ENCRYPT_KEY_STORE_ALIAS
ENCRYPT_KEY_STORE_PASSWORD
ENCRYPT_KEY_STORE_SECRET

測試方式和對稱加密的測試方式一致

 

詳細參考案例原始碼:https://gitee.com/coding-farmer/spirngcloud-learn