Config配置中心

服務(wù)配置現(xiàn)狀

  配置文件是我們再熟悉不過的,在微服務(wù)系統(tǒng)中,每個微服務(wù)不僅僅只有代碼,還需要連接其他資源,例如數(shù)據(jù)庫的配置或功能性的開關(guān) MySQL、Redis 、Security 等相關(guān)的配置。除了項(xiàng)目運(yùn)行的基礎(chǔ)配置之外,還有一些配置是與我們業(yè)務(wù)有關(guān)系的,比如說七牛存儲、短信和郵件相關(guān),或者一些業(yè)務(wù)上的開關(guān)。

  但是隨著微服務(wù)系統(tǒng)的不斷迭代,整個微服務(wù)系統(tǒng)可能會成為一個網(wǎng)狀結(jié)構(gòu),這個時候就要考慮整個微服務(wù)系統(tǒng)的擴(kuò)展性、伸縮性、耦合性等等。其中一個很重要的環(huán)節(jié)就是配置管理的問題。

常規(guī)配置管理解決方案缺點(diǎn)

硬編碼(需要修改代碼、繁瑣、風(fēng)險大)

properties 或者 yml(集群環(huán)境下需要替換和重啟)

xml(重新打包和重啟)

為什么使用 Spring Cloud Config

  由于常規(guī)配置管理有很大的缺點(diǎn),所以采用 Spring Cloud Config 集中式的配置中心來管理每個服務(wù)的配置信息。

  Spring Cloud Config 在微服務(wù)分布式系統(tǒng)中,采用 Server 服務(wù)端Client 客戶端的方式來提供可擴(kuò)展的配置服務(wù)。服務(wù)端提供配置文件的存儲,以接口的形式將配置文件的內(nèi)容提供出去;客戶端通過接口獲取數(shù)據(jù)、并依據(jù)此數(shù)據(jù)初始化自己的應(yīng)用。

  配置中心負(fù)責(zé)管理所有服務(wù)的各種環(huán)境配置文件。

  配置中心默認(rèn)采用 Git 的方式存儲配置文件,因此我們可以很容易的部署和修改,有助于環(huán)境配置進(jìn)行版本管理。

Spring Cloud Config 解決了什么問題

  Spring Cloud Config 解決了微服務(wù)配置的中心化、版本控制、平臺獨(dú)立、語言獨(dú)立等問題。其特性如下:

提供服務(wù)端和客戶端支持(Spring Cloud Config Server 和 Spring Cloud Config Client)

集中式管理分布式環(huán)境下的應(yīng)用部署

屬性值的加密和解密(對稱加密和非對稱加密)

基于 Spring 環(huán)境,無縫與 Spring 應(yīng)用集成

可用于任何語言開發(fā)的程序

默認(rèn)實(shí)現(xiàn)基于 Git ,可以進(jìn)行版本管理

  接下來,我們主要從以下幾塊來講一下 Config 的使用。

基礎(chǔ)版的配置中心(不集成 Eureka)

集成 Eureka 版的配置中心

基于 Actuator 實(shí)現(xiàn)配置的自動刷新

屬性值的加密和解密(對稱加密和非對稱加密)

基于 Spring Cloud Bus 實(shí)現(xiàn)配置的自動刷新

環(huán)境準(zhǔn)備

項(xiàng)目

  config-demo 聚合工程。

eureka-server:注冊中心(用于集成 Eureka 版的配置中心)

eureka-server02:注冊中心(用于集成 Eureka 版的配置中心)

order-service:訂單服務(wù)(用于集成 Eureka 版的配置中心)

倉庫

  config-repo 倉庫。

Repository name:倉庫名稱

Description(可選):倉庫描述介紹

Public,Private:倉庫權(quán)限(公開共享,私有或指定合作者)

Initialize this repository with a README:添加一個 README.md

Add .gitignore:不需要進(jìn)行版本管理的文件類型,生成對應(yīng)文件 .gitignore

Add a license:證書類型,生成對應(yīng)文件 LICENSE

配置文件

  不同環(huán)境的配置文件,上傳至 config-repo 倉庫。

配置文件的名稱不是亂起的,例如 config-client-dev.yml 和 config-client-prod.yml 這兩個文件是同一個項(xiàng)目的不同環(huán)境,項(xiàng)目名稱為 config-client, 一個對應(yīng)開發(fā)環(huán)境,一個對應(yīng)正式環(huán)境。test 表示測試環(huán)境。

  config-client.yml

server:

? port: 7777# 端口

?

spring:

? application:

?? name: config-client# 應(yīng)用名稱

# 自定義配置

name: config-client-default

  config-client-dev.yml

server:

? port: 7778# 端口

spring:

? application:

?? name: config-client# 應(yīng)用名稱

# 自定義配置

name: config-client-dev

  config-client-test.yml

server:

? port: 7779# 端口

spring:

? application:

?? name: config-client# 應(yīng)用名稱

# 自定義配置

name: config-client-test

  config-client-prod.yml

server:

? port: 7780# 端口

spring:

? application:

?? name: config-client# 應(yīng)用名稱

# 自定義配置

name: config-client-prod

入門案例

  入門案例講解:基礎(chǔ)版的配置中心(不集成 Eureka)

創(chuàng)建服務(wù)端

  在 config-demo 父工程下創(chuàng)建子項(xiàng)目 config-server。

添加依賴

  添加 spring-cloud-config-server 依賴,完整 pom.xml 文件如下:

<?xmlversion="1.0" encoding="UTF-8"?>

<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>

<artifactId>config-server</artifactId>

<version>1.0-SNAPSHOT</version>

<!-- 繼承父依賴 -->

<parent>

<groupId>com.example</groupId>

<artifactId>config-demo</artifactId>

<version>1.0-SNAPSHOT</version>

</parent>

<!-- 項(xiàng)目依賴 -->

<dependencies>

<!-- spring cloud config server 依賴 -->

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-config-server</artifactId>

</dependency>

<!-- spring boot test 依賴 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>org.junit.vintage</groupId>

<artifactId>junit-vintage-engine</artifactId>

</exclusion>

</exclusions>

</dependency>

</dependencies>

</project>

配置文件

server:

? port: 8888# 端

spring:

? application:

?? name: config-server# 應(yīng)用名稱

? cloud:

?? config:

? ?? server:

? ? ?? git:

? ? ? ?? uri: https://github.com/imrhelloworld/config-repo# 配置文件所在倉庫地址

#username: ? ? ? ? ? ? # Github 等產(chǎn)品的登錄賬號

#password: ? ? ? ? ? ? # Github 等產(chǎn)品的登錄密碼

#default-label: master # 配置文件分支

#search-paths: ? ? ? ? # 配置文件所在根目錄

啟動類

  啟動類添加 @EnableConfigServer 注解。

packagecom.example;

importorg.springframework.boot.SpringApplication;

importorg.springframework.boot.autoconfigure.SpringBootApplication;

importorg.springframework.cloud.config.server.EnableConfigServer;

// 配置中心服務(wù)端注解

@EnableConfigServer

@SpringBootApplication

publicclassConfigServerApplication{

publicstaticvoidmain(String[]args) {

SpringApplication.run(ConfigServerApplication.class,args);

訪問規(guī)則

  Spring Cloud Config 有一套訪問規(guī)則,我們通過這套規(guī)則在瀏覽器上直接訪問即可。

/{application}/{profile}[/{label}]

/{application}-{profile}.yml

/{label}/{application}-{profile}.yml

/{application}-{profile}.properties

/{label}/{application}-{profile}.properties

{application}:應(yīng)用名稱(目標(biāo)服務(wù)名稱)

{profile}:獲取指定環(huán)境配置,項(xiàng)目有開發(fā)環(huán)境、測試環(huán)境、生產(chǎn)環(huán)境,對應(yīng)到配置文件就是以 application-{profile}.yml 加以區(qū)分,例如 application-dev.yml、application-test.yml、application-prod.yml。默認(rèn)值為 default。

{label}:表示 git 分支,默認(rèn)是 master 分支,如果項(xiàng)目是以分支做區(qū)分也是可以的,那就可以通過不同的 label 來控制訪問不同的配置文件。

測試

  http://localhost:8888/config-client/default

  http://localhost:8888/config-client/dev/master

  http://localhost:8888/config-client-test.yml

  http://localhost:8888/master/config-client-prod.yml

  訪問以上地址,如果可以正常返回數(shù)據(jù),說明配置中心服務(wù)端一切正常。

創(chuàng)建客戶端

  在 config-demo 父工程下創(chuàng)建子項(xiàng)目 config-client。

添加依賴

  添加 spring-cloud-starter-config 依賴,完整 pom.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

? ? <modelVersion>4.0.0</modelVersion>

? ? <groupId>com.example</groupId>

? ? <artifactId>config-client</artifactId>

? ? <version>1.0-SNAPSHOT</version>

? ? <!-- 繼承父依賴 -->

? ? <parent>

? ? ? ? <groupId>com.example</groupId>

? ? ? ? <artifactId>config-demo</artifactId>

? ? ? ? <version>1.0-SNAPSHOT</version>

? ? </parent>

? ? <!-- 項(xiàng)目依賴 -->

? ? <dependencies>

? ? ? ? <!-- spring cloud starter config 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-starter-config</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot web 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot test 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>

? ? ? ? ? ? <scope>test</scope>

? ? ? ? ? ? <exclusions>

? ? ? ? ? ? ? ? <exclusion>

? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId>

? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId>

? ? ? ? ? ? ? ? </exclusion>

? ? ? ? ? ? </exclusions>

? ? ? ? </dependency>

? ? </dependencies>

</project>

配置文件

  客戶端配置文件名稱必須叫 bootstrap.yml

spring:

? cloud:

? ? config:

? ? ? name: config-client # 配置文件名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? uri: http://localhost:8888 # config-server 服務(wù)端地址

? ? ? label: master # git 分支

? ? ? profile: default # 指定環(huán)境

控制層

  添加一個 RestController 用于測試獲取配置文件信息。

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class ConfigController {

? ? @Value("${name}")

? ? private String name;

? ? @GetMapping("/name")

? ? public String getName() {

? ? ? ? return name;

? ? }

}

啟動類

package com.example;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class ConfigClientApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication.run(ConfigClientApplication.class, args);

? ? }

}

測試

  訪問:http://localhost:7777/name 結(jié)果如下:

  修改配置文件為 dev 環(huán)境:

spring:

? cloud:

? ? config:

? ? ? name: config-client # 應(yīng)用名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? uri: http://localhost:8888 # config-server 服務(wù)端地址

? ? ? label: master # git 分支

? ? ? profile: dev # 指定環(huán)境

  訪問:http://localhost:7778/name 結(jié)果如下:

Spring Cloud Config 高可用

  以上講了 Spring Cloud Config 最基礎(chǔ)的用法,如果我們的項(xiàng)目中使用了 Eureka 作為服務(wù)注冊發(fā)現(xiàn)中心,那么 Spring Cloud Config 也應(yīng)該注冊到 Eureka,方便其他服務(wù)使用,并且可以注冊多個配置中心服務(wù)端,實(shí)現(xiàn)高可用。

  接下來就集成 Spring Cloud Config 到 Eureka。關(guān)于 Eureka 的相關(guān)知識大家可翻閱我的歷史文章進(jìn)行學(xué)習(xí)。

添加配置文件

  在 Github 倉庫中增加配置文件。

  order-service-dev.yml

server:

? port: 9090 # 端口

spring:

? application:

? ? name: order-service # 應(yīng)用名稱

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? service-url:? ? ? ? ? ? ? ? ? # 設(shè)置服務(wù)注冊中心地址

? ? ? defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

# 自定義配置

name: order-service-dev

  order-service-prod.yml

server:

? port: 9091 # 端口

spring:

? application:

? ? name: order-service # 應(yīng)用名稱

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? service-url:? ? ? ? ? ? ? ? ? # 設(shè)置服務(wù)注冊中心地址

? ? ? defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

# 自定義配置

name: order-service-prod

整合注冊中心

  案例已經(jīng)給大家準(zhǔn)備好了,無需創(chuàng)建注冊中心直接使用即可,為了清楚,把依賴和配置信息給大家貼出來。

依賴

  eureka-server 和 eureka-server02 核心依賴部分一致。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

? ? <modelVersion>4.0.0</modelVersion>

? ? <groupId>com.example</groupId>

? ? <artifactId>eureka-server</artifactId>

? ? <version>1.0-SNAPSHOT</version>

? ? <!-- 繼承父依賴 -->

? ? <parent>

? ? ? ? <groupId>com.example</groupId>

? ? ? ? <artifactId>config-demo</artifactId>

? ? ? ? <version>1.0-SNAPSHOT</version>

? ? </parent>

? ? <!-- 項(xiàng)目依賴 -->

? ? <dependencies>

? ? ? ? <!-- netflix eureka server 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot web 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot test 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>

? ? ? ? ? ? <scope>test</scope>

? ? ? ? ? ? <exclusions>

? ? ? ? ? ? ? ? <exclusion>

? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId>

? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId>

? ? ? ? ? ? ? ? </exclusion>

? ? ? ? ? ? </exclusions>

? ? ? ? </dependency>

? ? </dependencies>

</project>

配置文件

  eureka-server 的 application.yml

server:

? port: 8761 # 端口

spring:

? application:

? ? name: eureka-server # 應(yīng)用名稱(集群下相同)

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? hostname: eureka01? ? ? ? ? ? # 主機(jī)名,不配置的時候?qū)⒏鶕?jù)操作系統(tǒng)的主機(jī)名來獲取

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? # 設(shè)置服務(wù)注冊中心地址,指向另一個注冊中心

? ? service-url:? ? ? ? ? ? ? ? ? # 注冊中心對外暴露的注冊地址

? ? ? defaultZone: http://localhost:8762/eureka/

  eureka-server02 的 application.yml

server:

? port: 8762 # 端口

spring:

? application:

? ? name: eureka-server # 應(yīng)用名稱(集群下相同)

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? hostname: eureka02? ? ? ? ? ? # 主機(jī)名,不配置的時候?qū)⒏鶕?jù)操作系統(tǒng)的主機(jī)名來獲取

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? # 設(shè)置服務(wù)注冊中心地址,指向另一個注冊中心

? ? service-url:? ? ? ? ? ? ? ? ? # 注冊中心對外暴露的注冊地址

? ? ? defaultZone: http://localhost:8761/eureka/

啟動類

  eureka-server 和 eureka-server02 啟動類核心代碼一致。

package com.example;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication

// 開啟 EurekaServer 注解

@EnableEurekaServer

public class EurekaServerApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication.run(EurekaServerApplication.class, args);

? ? }

}

Spring Cloud Config 服務(wù)端

  服務(wù)端和基礎(chǔ)版的配置中心相比多了 Eureka 的配置,其他地方都是一樣的。

  config-server 服務(wù)端構(gòu)建完成以后再復(fù)刻一個 config-server02 實(shí)現(xiàn)高可用。

依賴

  config-server 和 config-server02 核心依賴部分一致。注意是 spring-cloud-config-server 依賴。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

? ? <modelVersion>4.0.0</modelVersion>

? ? <groupId>com.example</groupId>

? ? <artifactId>config-server</artifactId>

? ? <version>1.0-SNAPSHOT</version>

? ? <!-- 繼承父依賴 -->

? ? <parent>

? ? ? ? <groupId>com.example</groupId>

? ? ? ? <artifactId>config-demo</artifactId>

? ? ? ? <version>1.0-SNAPSHOT</version>

? ? </parent>

? ? <!-- 項(xiàng)目依賴 -->

? ? <dependencies>

? ? ? ? <!-- spring cloud config server 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-config-server</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- netflix eureka client 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot test 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>

? ? ? ? ? ? <scope>test</scope>

? ? ? ? ? ? <exclusions>

? ? ? ? ? ? ? ? <exclusion>

? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId>

? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId>

? ? ? ? ? ? ? ? </exclusion>

? ? ? ? ? ? </exclusions>

? ? ? ? </dependency>

? ? </dependencies>

</project>

配置文件

  config-server 的 application.yml

server:

? port: 8888 # 端口

spring:

? application:

? ? name: config-server # 應(yīng)用名稱

? cloud:

? ? config:

? ? ? server:

? ? ? ? git:

? ? ? ? ? uri: https://github.com/imrhelloworld/config-repo # 配置文件所在倉庫地址

? ? ? ? ? #username:? ? ? ? ? ? # Github 等產(chǎn)品的登錄賬號

? ? ? ? ? #password:? ? ? ? ? ? # Github 等產(chǎn)品的登錄密碼

? ? ? ? ? #default-label: master # 配置文件分支

? ? ? ? ? #search-paths:? ? ? ? # 配置文件所在根目錄

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? service-url:? ? ? ? ? ? ? ? ? # 設(shè)置服務(wù)注冊中心地址

? ? ? defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

  config-server02 的 application.yml

server:

? port: 8889 # 端口

spring:

? application:

? ? name: config-server # 應(yīng)用名稱

? cloud:

? ? config:

? ? ? server:

? ? ? ? git:

? ? ? ? ? uri: https://github.com/imrhelloworld/config-repo # 配置文件所在倉庫地址

? ? ? ? ? #username:? ? ? ? ? ? # Github 等產(chǎn)品的登錄賬號

? ? ? ? ? #password:? ? ? ? ? ? # Github 等產(chǎn)品的登錄密碼

? ? ? ? ? #default-label: master # 配置文件分支

? ? ? ? ? #search-paths:? ? ? ? # 配置文件所在根目錄

# 配置 Eureka Server 注冊中心

eureka:

? instance:

? ? prefer-ip-address: true? ? ? # 是否使用 ip 地址注冊

? ? instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

? client:

? ? service-url:? ? ? ? ? ? ? ? ? # 設(shè)置服務(wù)注冊中心地址

? ? ? defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

啟動類

  config-server 和 config-server02 啟動類核心代碼一致。

package com.example;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.config.server.EnableConfigServer;

// 開啟 EurekaClient 注解,當(dāng)前版本如果配置了 Eureka 注冊中心,默認(rèn)會開啟該注解

//@EnableEurekaClient

// 配置中心服務(wù)端注解

@EnableConfigServer

@SpringBootApplication

public class ConfigServerApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication.run(ConfigServerApplication.class, args);

? ? }

}

Spring Cloud Config 客戶端

  客戶端加入 Eureka 以后,就不用直接和配置中心服務(wù)端打交道了,而是通過 Eureka 來訪問。

依賴

  order-service 的 pom.xml。注意是 spring-cloud-starter-config 依賴。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

? ? <modelVersion>4.0.0</modelVersion>

? ? <groupId>com.example</groupId>

? ? <artifactId>order-service</artifactId>

? ? <version>1.0-SNAPSHOT</version>

? ? <!-- 繼承父依賴 -->

? ? <parent>

? ? ? ? <groupId>com.example</groupId>

? ? ? ? <artifactId>config-demo</artifactId>

? ? ? ? <version>1.0-SNAPSHOT</version>

? ? </parent>

? ? <!-- 項(xiàng)目依賴 -->

? ? <dependencies>

? ? ? ? <!-- spring boot web 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- netflix eureka client 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring cloud starter config 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.cloud</groupId>

? ? ? ? ? ? <artifactId>spring-cloud-starter-config</artifactId>

? ? ? ? </dependency>

? ? ? ? <!-- spring boot test 依賴 -->

? ? ? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>

? ? ? ? ? ? <scope>test</scope>

? ? ? ? ? ? <exclusions>

? ? ? ? ? ? ? ? <exclusion>

? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId>

? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId>

? ? ? ? ? ? ? ? </exclusion>

? ? ? ? ? ? </exclusions>

? ? ? ? </dependency>

? ? </dependencies>

</project>

配置文件

  order-service 的 bootstrap.yml

spring:

? cloud:

? ? config:

? ? ? name: order-service # 配置文件名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? label: master # git 分支

? ? ? profile: dev # 指定環(huán)境

? ? ? discovery:

? ? ? ? enabled: true # 開啟

? ? ? ? service-id: config-server # 指定配置中心服務(wù)端的 service-id

控制層

  添加一個 RestController 用于測試獲取配置文件信息。

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class ConfigController {

? ? @Value("${name}")

? ? private String name;

? ? @GetMapping("/name")

? ? public String getName() {

? ? ? ? return name;

? ? }

}

啟動類

package com.example;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

// 開啟 EurekaClient 注解,當(dāng)前版本如果配置了 Eureka 注冊中心,默認(rèn)會開啟該注解

//@EnableEurekaClient

@SpringBootApplication

public class OrderServiceApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication.run(OrderServiceApplication.class, args);

? ? }

}

測試

  啟動注冊中心 eureka-server 和 eureka-server02。

  啟動配置中心服務(wù)端 config-server。

  啟動配置中心客戶端 order-service。

  當(dāng)前環(huán)境在 Eureka UI 界面中如下:

  訪問:http://localhost:9090/name 結(jié)果如下:

配置中心工作原理

  開發(fā)人員將配置文件存儲至 Git 遠(yuǎn)程倉庫,或后期對 Git 遠(yuǎn)程倉庫的文件進(jìn)行修改。如果遠(yuǎn)程倉庫發(fā)生了版本改變,Config Server 會將 Git 遠(yuǎn)程倉庫中的文件同步至本地倉庫中。大家仔細(xì)觀察 Config Server 的控制臺可以看到類似如下代碼。

[nio-8888-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository? : Adding property source: file:/C:/Users/MRHELL~1/AppData/Local/Temp/config-repo-17506367621853740906/order-service-dev.yml

  為什么要這么做呢?因?yàn)槲覀円紤]網(wǎng)絡(luò)波動的情況下,無法訪問遠(yuǎn)程倉庫的問題。

配置中心自動刷新

  Spring Cloud Config 在項(xiàng)目啟動時才會加載配置內(nèi)容這一機(jī)制,導(dǎo)致了它存在一個缺陷,修改配置文件內(nèi)容后,不會自動刷新。例如我們上面的項(xiàng)目,當(dāng)服務(wù)已經(jīng)啟動的時候,修改 Github 上的配置文件內(nèi)容,這時候,再次刷新頁面,對不起,還是舊的配置內(nèi)容,新內(nèi)容不會主動刷新過來。

  訪問:http://localhost:9090/name 結(jié)果如下:

  重啟 Config Client 以后,訪問:http://localhost:9090/name 結(jié)果如下:

  但是,總不能每次修改了配置后重啟服務(wù)吧。如果是那樣的話,還是不要用它為好,直接用本地配置文件豈不更快。

  它提供了一個刷新機(jī)制,但是需要我們主動觸發(fā)。那就是 @RefreshScope 注解并結(jié)合 Actuator,注意要引入 spring-boot-starter-actuator。

添加依賴

  Config Client 添加 spring-boot-starter-actuator 依賴。

<!-- spring boot actuator 依賴 -->

<dependency>

? ? <groupId>org.springframework.boot</groupId>

? ? <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

配置文件

  其實(shí)這里主要用到的是 refresh 這個端點(diǎn)。以下為 Config Client 的 bootstrap.yml

spring:

? cloud:

? ? config:

? ? ? name: order-service # 配置文件名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? uri: http://localhost:8888 # config-server 服務(wù)端地址

? ? ? label: master # git 分支

? ? ? profile: dev # 指定環(huán)境

? ? ? discovery:

? ? ? ? enabled: true # 開啟

? ? ? ? service-id: config-server # 指定配置中心服務(wù)端的 service-id

# 度量指標(biāo)監(jiān)控與健康檢查

management:

? endpoints:

? ? web:

? ? ? base-path: /actuator? ? # 訪問端點(diǎn)根路徑,默認(rèn)為 /actuator

? ? ? exposure:

? ? ? ? include: '*'? ? ? ? ? # 需要開啟的端點(diǎn),這里主要用到的是 refresh 這個端點(diǎn)

? ? ? ? #exclude:? ? ? ? ? ? # 不需要開啟的端點(diǎn)

控制層

  在需要讀取配置的類上增加 @RefreshScope 注解。

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.cloud.context.config.annotation.RefreshScope;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RefreshScope

@RestController

public class ConfigController {

? ? @Value("${name}")

? ? private String name;

? ? @GetMapping("/name")

? ? public String getName() {

? ? ? ? return name;

? ? }

}

測試

  重啟 Config Client,訪問:http://localhost:9090/actuator 可以看到 refresh 端點(diǎn)已開啟。

  修改 Github 上的配置文件內(nèi)容并提交,訪問:http://localhost:9090/name,沒有反應(yīng),不慌。

  接下來,我們發(fā)送 POST 請求到 http://localhost:9090/actuator/refresh 這個接口,用 Postman 之類的工具即可。

  再次訪問:http://localhost:9090/name 結(jié)果如下:

在 Github 中配置 Webhook

  這就結(jié)束了嗎,并沒有,總不能每次改了配置后,就用 Postman 訪問一下 refresh 接口吧,還是不夠方便呀。

  Github 提供了一種 Webhook 的方式,當(dāng)有代碼變更的時候,會調(diào)用我們設(shè)置的地址,來實(shí)現(xiàn)我們想達(dá)到的目的。

  進(jìn)入 Github 倉庫配置頁面,選擇 Webhooks ,并點(diǎn)擊 Add webhook。

  填寫回調(diào)的地址,也就是上面提到的 actuator/refresh 這個地址,但是必須保證這個地址是可以被 Github 訪問的。如果是內(nèi)網(wǎng)就沒辦法了。一般公司內(nèi)的項(xiàng)目都會有自己的代碼管理工具,例如自建的 gitlab,gitlab 也有 webhook 的功能,這樣就可以調(diào)用到內(nèi)網(wǎng)的地址了。

  還有一種辦法就是使用 spring-cloud-config-monitor,然后調(diào)用 /monitor 接口完成動態(tài)刷新。

Spring Cloud Bus 自動刷新

  如果只有一個 Config Client 的話,那我們用 Webhook,設(shè)置手動刷新都不算太費(fèi)事,但是如果客戶端比較多的情況下,一個一個去手動刷新未免有點(diǎn)復(fù)雜。我們可以借助 Spring Cloud Bus 的廣播功能,讓 Config Client 都訂閱配置更新事件,當(dāng)配置更新時,觸發(fā)其中一個端的更新事件,Spring Cloud Bus 就把此事件廣播到其他訂閱客戶端,以此來達(dá)到批量更新。

  為了方便大家學(xué)習(xí)和整理,這部分的知識我們在微服務(wù)系列之 Spring Cloud Bus 中單獨(dú)給大家講解。記得關(guān)注噢 ~

配置中心加解密

  考慮這樣一個問題:所有的配置文件都存儲在 Git 遠(yuǎn)程倉庫,配置文件中的一些信息又是比較敏感的。所以,我們需要對這些敏感信息進(jìn)行加密處理。主要的加密方法分為兩種:一種是共享密鑰加密(對稱密鑰加密),一種是公開密鑰加密(非對稱密鑰加密)。

對稱加解密 Symmetric encryption

  對稱加密是最快速、最簡單的一種加密方式,加密(encryption)與解密(decryption)用的是同樣的密鑰(secret key)。

檢查加密環(huán)境

版本問題

  訪問 Config Server:http://localhost:8888/encrypt/status

  檢查結(jié)果如果是:{"description":"No key was installed for encryption service","status":"NO_KEY"} 說明沒有為加密服務(wù)安裝密鑰,也說明你使用的是較低的 JDK 版本。

  比較簡單的解決辦法:更換高版本 JDK,比如使用最新版的 LTS 版本 JDK-11.0.6。

  復(fù)雜的解決辦法:從 Oracle 官網(wǎng)下載對應(yīng) JCE,下載鏈接:https://www.oracle.com/java/technologies/javase-jce-all-downloads.html

  下圖紅色框中內(nèi)容已經(jīng)足夠說明原因:JDK 9 以及更高版本已附帶策略文件,并在默認(rèn)情況下啟用。

  如果你的當(dāng)前環(huán)境必須使用低版本 JDK,那么請下載對應(yīng) JCE 壓縮包,下載解壓后把 local_policy.jar 和 US_export_policy.jar 文件安裝到需要安裝 JCE 機(jī)器上的 JDK 或 JRE 的 security 目錄下即可。

配置問題

  檢查結(jié)果如果是:{"description":"The encryption algorithm is not strong enough","status":"INVALID"} 說明服務(wù)端未配置加密。

  Config Server 創(chuàng)建配置文件,注意必須叫 bootstrap.yml,配置密鑰信息即可。

# 密鑰

encrypt:

? key: example

  重啟 Config Server 訪問:http://localhost:8888/encrypt/status 結(jié)果如下:

加解密演示

配置中心服務(wù)端

  使用 curl 命令訪問 /encrypt 端點(diǎn)對屬性值 root 進(jìn)行加密。反向操作 /decrypt 可解密。

curl http://localhost:8888/encrypt -d root

  加密結(jié)果:bfb5cf8d7cab63e4b770b76d4e96c3a57d40f7c9df13612cb3134e2f7ed26123

  解密

Git 倉庫

  把加密后的數(shù)據(jù)更新到 Git 遠(yuǎn)程倉庫的配置文件中。值得注意的是需要在加密結(jié)果前添加 {cipher} 串,如果遠(yuǎn)程屬性源包含加密的內(nèi)容(以開頭的值{cipher}),則將其解密,然后再通過HTTP發(fā)送給客戶端。

配置中心客戶端

  Config Client 控制層添加獲取配置信息代碼。

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.cloud.context.config.annotation.RefreshScope;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RefreshScope

@RestController

public class ConfigController {

? ? @Value("${name}")

? ? private String name;

? ? @Value("${password}")

? ? private String password;

? ? @GetMapping("/name")

? ? public String getName() {

? ? ? ? return name;

? ? }

? ? @GetMapping("/password")

? ? public String getPassword() {

? ? ? ? return password;

? ? }

}

  修改 Config Client 配置文件,重啟測試。

spring:

? cloud:

? ? config:

? ? ? name: order-service # 配置文件名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? label: master # git 分支

? ? ? profile: prod # 指定環(huán)境

? ? ? discovery:

? ? ? ? enabled: true # 開啟

? ? ? ? service-id: config-server # 指定配置中心服務(wù)端的 service-id

# 度量指標(biāo)監(jiān)控與健康檢查

management:

? endpoints:

? ? web:

? ? ? base-path: /actuator? ? # 訪問端點(diǎn)根路徑,默認(rèn)為 /actuator

? ? ? exposure:

? ? ? ? include: '*'? ? ? ? ? # 需要開啟的端點(diǎn),這里主要用到的是 refresh 這個端點(diǎn)

? ? ? ? #exclude:? ? ? ? ? ? # 不需要開啟的端點(diǎn)

  訪問:http://localhost:9091/password 返回解密后的結(jié)果。

非對稱加解密 Asymmetric encryption

對稱加密和非對稱加密的區(qū)別

  對稱加密算法在加密和解密時使用的是同一個密鑰。只要拿到密鑰,任何人都能破解。

  非對稱加密算法需要兩個密鑰來進(jìn)行加密和解密,這兩個密鑰分別是公開密鑰(public key 簡稱公鑰)和私有密鑰(private key 簡稱私鑰)。在傳輸過程中,即使攻擊者截獲了傳輸?shù)拿芪模⒌玫搅斯€,也無法破解密文,因?yàn)槭褂脤S妹荑€才能破解密文。

  圖片取自圖解HTTP一書。

Java-keytool 使用說明

  Keytool 用來管理私鑰倉庫(keystore)和與之相關(guān)的X.509證書鏈(用以驗(yàn)證與私鑰對應(yīng)的公鑰),也可以用來管理其他信任實(shí)體。

  默認(rèn)大家都配置了 Java 的環(huán)境變量,打開 CMD 窗口運(yùn)行以下命令。

# 生成名為 config.keystore 的 keystore 文件,別名為 config,加密算法類型使用 RSA,密鑰庫口令和密鑰口令均為:config

keytool -genkeypair -keystore config.keystore -alias config -keyalg RSA -keypass config -storepass config

  此時在我的 D 盤下會生成一個 config.keystore 文件。

加解密演示

配置中心服務(wù)端

  將 config.keystore 文件添加至 Config Server 項(xiàng)目 resources 目錄中。

  創(chuàng)建 bootstrap.yml 添加非對稱加解密配置。注意:值要跟 CMD 里輸入的值對應(yīng)不然會出錯。

# 非對稱加解密

encrypt:

? key-store:

? ? location: classpath:config.keystore # keystore 文件存儲路徑

? ? alias: config # 密鑰對別名

? ? password: config # storepass 密鑰倉庫

? ? secret: config # keypass 用來保護(hù)所生成密鑰對中的私鑰

  pom.xml 添加避免 maven 過濾文件的配置。

<!-- build標(biāo)簽 常用于添加插件及編譯配置 -->

<build>

? ? <!-- 讀取配置文件 -->

? ? <resources>

? ? ? ? <resource>

? ? ? ? ? ? <directory>src/main/resources</directory>

? ? ? ? </resource>

? ? ? ? <resource>

? ? ? ? ? ? <directory>src/main/java</directory>

? ? ? ? ? ? <includes>

? ? ? ? ? ? ? ? <include>**/*.xml</include>

? ? ? ? ? ? ? ? <include>**/*.properties</include>

? ? ? ? ? ? ? ? <include>**/*.tld</include>

? ? ? ? ? ? ? ? <include>**/*.keystore</include>

? ? ? ? ? ? </includes>

? ? ? ? ? ? <filtering>false</filtering>

? ? ? ? </resource>

? ? </resources>

</build>

  檢查加密環(huán)境,訪問:http://localhost:8889/encrypt/status 結(jié)果如下:

  使用 curl 命令訪問 /encrypt 端點(diǎn)對屬性值 root 進(jìn)行加密。反向操作 /decrypt 可解密。

curl http://localhost:8889/encrypt -d root

  加密結(jié)果:AQCrWHuNel3mhC0sfF2QqMtDAU4GUmanLmpPk0jn0ptGeYn2wSIEFrEg9UWcXQEU90kFfsE3t1iZG2LIolU4f8DVN3jjxIVm0fh+Vc4MlCHZqOH+Y6RffK3dS09ixmOpMK5otdMrfC/IR0od6xXJshcu5rwFBR7PN5CW+Gb97Tcjw+ooy51pLBCtVo9Xqu9qNrYdiYMnq7vb++PaGpZZcLhIht1YjIrlcDepW9N9Wlhy9Vg1CDI0mWM07nVbEh5yEmCyIiTuA41baRsQrcR4TLOtF3kSRUPi/ysTO/NEJ1lpO/VcuQri/YQOZ20WYNr5MnBrAfjdXPg6uonGyIAF8dIpVEdWut8BWl3Dp+qeoLv8HHV0R7njABvoCZK8ZhFzIC0=

  解密

Git 倉庫

  把加密后的數(shù)據(jù)更新到 Git 遠(yuǎn)程倉庫的配置文件中。值得注意的是需要在加密結(jié)果前添加 {cipher} 串,如果遠(yuǎn)程屬性源包含加密的內(nèi)容(以開頭的值{cipher}),則將其解密,然后再通過HTTP發(fā)送給客戶端。

配置中心客戶端

  Config Client 配置文件如下。

spring:

? cloud:

? ? config:

? ? ? name: order-service # 配置文件名稱,對應(yīng) git 倉庫中配置文件前半部分

? ? ? label: master # git 分支

? ? ? profile: prod # 指定環(huán)境

? ? ? discovery:

? ? ? ? enabled: true # 開啟

? ? ? ? service-id: config-server # 指定配置中心服務(wù)端的 service-id

# 度量指標(biāo)監(jiān)控與健康檢查

management:

? endpoints:

? ? web:

? ? ? base-path: /actuator? ? # 訪問端點(diǎn)根路徑,默認(rèn)為 /actuator

? ? ? exposure:

? ? ? ? include: '*'? ? ? ? ? # 需要開啟的端點(diǎn),這里主要用到的是 refresh 這個端點(diǎn)

? ? ? ? #exclude:? ? ? ? ? ? # 不需要開啟的端點(diǎn)

  訪問:http://localhost:9091/password 返回解密后的結(jié)果。

配置中心用戶安全認(rèn)證

  折騰了大半天終于給大家把加解密講完了,但是如果你夠仔細(xì),你會發(fā)現(xiàn)此時的 Config Server 誰都可以訪問,而且直接通過 Config Server 訪問配置文件信息,加密的內(nèi)容就會解密后直接顯示在瀏覽器中,這豈不是又白折騰了?當(dāng)然不是,我們只需要添加用戶安全認(rèn)證即可。

添加依賴

  Config Server 添加 security 依賴。

<!-- spring boot security 依賴 -->

<dependency>

? ? <groupId>org.springframework.boot</groupId>

? ? <artifactId>spring-boot-starter-security</artifactId>

</dependency>

配置文件

  Config Server 的 application.yml 添加安全認(rèn)證配置。

spring:

? # 安全認(rèn)證

? security:

? ? user:

? ? ? name: user

? ? ? password: 123456

  Config Client 的 bootstrap.yml 添加安全認(rèn)證配置。

spring:

? cloud:

? ? config:

? ? ? # 安全認(rèn)證

? ? ? username: user

? ? ? password: 123456

測試

服務(wù)端

  Config Server 訪問:http://localhost:8889/order-service-prod.yml 被重定向至登錄頁。

  輸入用戶名和密碼后,結(jié)果如下:

客戶端

  Config Client 訪問:http://localhost:9091/password 結(jié)果如下:

  至此 Config 配置中心所有的知識點(diǎn)就講解結(jié)束了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容