一、簡介 Spring Cloud Confg 是用來為分散式系統中的基礎設施和微服務應用提供集中化的外部配置支持,它分為服務端與客戶端兩個部分。其中服務端也稱為分散式配置中心,它是一個獨立的微服務應用,用來連接配置倉庫併為客戶端提供獲取配置信息、加密/解密信息等訪問介面;而客戶端則是微 ...
一、簡介
Spring Cloud Confg 是用來為分散式系統中的基礎設施和微服務應用提供集中化的外部配置支持,它分為服務端與客戶端兩個部分。其中服務端也稱為分散式配置中心,它是一個獨立的微服務應用,用來連接配置倉庫併為客戶端提供獲取配置信息、加密/解密信息等訪問介面;而客戶端則是微服務架構中的各個微服務應用或基礎設施,它們通過指定的配置中心來管理應用資源與業務相關的配置內容,併在啟動的時候從配置中心獲取和載入配置信息。
二、Spring Config Server
搭建一個 Config Server,首先需要一個倉庫,作為分散式配置中心的存儲。這裡我們選擇了 Github 作為我們的倉庫:https://github.com/JMCuixy/cloud-config-server/tree/master/config-repo
1. pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!--啟動 security 保護,不需要可不添加-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
2. application.yml
server:
port: 7001
spring:
application:
name: cloud-config-server
# 配置完成後可訪問的 url 如下,比如:http://localhost:7001/env/default
# /{application}/{profile} [/{label}]
# /{application}-{profile}.yml
# /{label}/{application}-{profile}.yml
# /{application}-{profile}.properties
# /{label}/{application}-{profile}.properties
cloud:
config:
# 為配置中心提供安全保護
username: user
password: password
server:
git:
# 倉庫地址
uri: https://github.com/JMCuixy/cloud-config-server.git
# 搜索路徑
search-paths: config-repo
# 訪問 http://localhost:7001/actuator/health 可以獲取配置中心健康指標
health:
repositories:
env:
name: env
profiles: default
label: master
env-dev:
name: env-dev
profiles: dev
label: master
env-test:
name: env-test
profiles: test
label: master
env-prod:
name: env-prod
profiles: prod
label: master
# 提供 security 保護
security:
user:
name: user
password: password
management:
endpoint:
health:
enabled: true
show-details: always
eureka:
client:
service-url:
defaultZone: http://user:password@localhost:1111/eureka/
這裡我沒有配置 Github 的 username 和 password,用的是 SSH key 的方式。
3. ConfigApplication.java
// 開啟 Spring Cloud Config 的 Server 功能
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
至此,一個 Spring Cloud Config Server 就搭建完成了。上面的配置中,我們將 Config Server 註冊到 Eureka Server 中,當作整個系統服務的一部分,所以Config Client 只要利用 Eureka 的服務發現維持與 Config Server 通信就可以了。
在Config Server 的文件系統中,每次客戶端請求獲取配置信息時,Confg Server 從 Git 倉庫中獲取最新配置到本地,然後在本地 Git 倉庫中讀取並返回。當遠程倉庫無法獲取時,直接將本地內容返回。
二、Spring Config Client
Spring Cloud Confg 的客戶端在啟動的時候,預設會從工程的 classpath 中載入配置信息並啟動應用。只有當我們配置 spring.cloud.config.uri(或者spring.cloud.config.discovery) 的時候,客戶端應用才會嘗試連接 Spring Cloud Confg 的服務端來獲取遠程配置信息並初始化 Spring 環境配置。同時,我們必須將該參數配置在bootstrap.yml、環境變數或是其他優先順序高於應用 Jar 包內的配置信息中,才能正確載入到遠程配置。
1. pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 當連接 config-server 失敗的時候,可增加重試-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!--配置動態刷新-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2. bootstrap.yml 和 application.yml
- bootstrap.yml
spring:
application:
# 對應配置文件規則中的 {application} 部分
name: env
cloud:
config:
name: env
# uri: http://localhost:7001
discovery:
enabled: true
service-id: cloud-config-server
# 環境變數
profile: default
# 分支
label: master
# config Server 配置的安全信息
username: user
password: password
# 快速失敗響應(當發現 config-server 連接失敗時,就不做連接的準備工作,直接返回失敗)
fail-fast: true
# 失敗重試
retry:
# 初始重試間隔時間,毫秒
initial-interval: 1000
# 下一間隔的乘數
multiplier: 1.1
# 最大間隔時間
max-interval: 2000
# 最多重試次數
max-attempts: 6
bootstrap 配置會系統會優先載入,載入優先順序比 application 高。
- application.yml
server:
port: 7002
spring:
application:
name: cloud-config-client
eureka:
client:
service-url:
defaultZone: http://user:password@localhost:1111/eureka/
management:
endpoints:
web:
exposure:
# 開啟指定端點
# 配置刷新地址:POST http://127.0.0.1:7002/actuator/refresh
include: 'refresh'
3. ConfigClientApplication.java
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
4. 應用
接下來瞅瞅客戶端要怎麼讀到伺服器的配置項呢?
@RefreshScope
@RestController
public class ConfigClientAdmin {
@Value("${from:default}")
private String from;
@Autowired
private Environment environment;
@RequestMapping("/from")
public String from() {
String fromEnv = environment.getProperty("from");
return from + "_" + fromEnv;
}
}
如上,我們可以使用 @Value 註解註入配置信息,或者使用 Environment Bean 來獲取配置項。
需要註意的是,當服務端的配置項更新的時候,客戶端並不會同步獲得更新,需要 Post 方法執行 "/actuator/refresh" 來刷新配置項。
@RefreshScope 註解使配置的內容動態化,當使用 http://127.0.0.1:7002/actuator/refresh 刷新配置的時候,會刷新帶有 @RefreshScope 的 Bean。
三、動態路由
上一篇文章 我們嘗試用 Spring Cloud Zuul 搭建了網關服務,但是我們發現路由信息都配置在 application.yml 中,這對網關的高可用是個不小的打擊,因為網關作為系統流量的路口,總不能因為改個路由信息天天重啟網關吧?所以動態路由的實現,就變得迫不及待了,好在我們現在有了 Spring Cloud Config。
首先,我們將 Spring Cloud Zuul 的路由信息,配置在 Config Server 的 env.yml 中:
zuul:
routes:
client-1:
# ?:匹配任意單個數量字元;*:匹配任意多個數量字元;**:匹配任意多個數量字元,支持多級目錄
# 使用 url 的配置沒有線程隔離和斷路器的自我保護功能,不推薦使用
path: /client-1/**
url: http://localhost:2222/
# 敏感頭信息設置為空,表示不過濾敏感頭信息,允許敏感頭信息滲透到下游伺服器
sensitiveHeaders: ""
customSensitiveHeaders: true
client-2:
path: /client-2/**
serviceId: cloud-eureka-client
# zuul.routes.<serviceid> = <path>
cloud-eureka-client: /client-3/**
client-4:
path: /client-4/**
# 請求轉發 —— 僅限轉發到本地介面
url: forward:/local
# Zuul 將對所有的服務都不自動創建路由規則
ignored-services: "*"
# 對某些 url 設置不經過路由選擇
ignored-patterns: {"/**/world/**","/**/zuul/**"}
# Spring Cloud Zuul在請求路由時,會過濾掉 HTTP 請求頭(Cookie、Set-Cookie、Authorization)信息中的一些敏感信息,
sensitive-headers: {"Cookie", "Set-Cookie", "Authorization"}
# 網關在進行路由轉發時為請求設置 Host 頭信息(保持在路由轉發過程中 host 頭信息不變)
add-host-header: true
# 請求轉發時加上 X-Forwarded-*頭域
add-proxy-headers: true
# 是否開啟重試,預設關閉
retryable: true
# 通過 /zuul 路徑訪問的請求會繞過 dispatcherServlet, 被 Zuu1Servlet 處理,主要用來應對處理大文件上傳的情況。
servlet-path: /zuul
# 禁用某個過濾器 zuul.<SimpleClassName>.<filterTye>.disable=true
TokenFilter:
pre:
disable: true
然後,我們將網關服務註冊為 Config Client(配置項與上面類似,就不贅述了),從 Config Server 獲取路由信息:
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class DynamicRouteApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicRouteApplication.class, args);
}
/**
* 刷新地址:POST http://127.0.0.1:5006/actuator/refresh
* 路由查看地址:GET http://127.0.0.1:5006/actuator/routes
*
* @return
*/
@Bean
@Primary
//該註解來使 zuul 的配置內容動態化
@RefreshScope
@ConfigurationProperties(prefix = "zuul")
public ZuulProperties zuulProperties() {
return new ZuulProperties();
}
}
這樣,就把我們的路由信息交給 Config Server 去管理了~~