02. Spring Cloud--配置服務
目錄
2.1 配置管理的重要性
對於在雲中執行的微服務,管理應用程式配置是至關重要的,因為微服務例項需要以最少的人為干預快速啟動。每當人們需要手動配置或接觸服務以實現部署時,都有可能出現配置漂移、意外中斷以及應用程式響應可伸縮性出現延遲的情況。
配置漂移:
當構建好的服務被部署在伺服器上後,由於有人登陸該伺服器並修改了一個東西,導致該服務的配置被更改,從而使機器上的實際配置與原始碼管理的配置不一致。這種現象就叫配置漂移。
通過4條原則,我們來開始有關應用程式配置管理的討論:
- 分離 – 我們希望將服務配置資訊與服務的實際物理部署完全分開。應用程式配置不應與服務例項一起部署。相反,配置資訊應該作為環境變數傳遞給正在啟動的服務,或者在服務啟動時從集中式儲存庫中讀取。
- 抽象 – 將訪問配置資料的功能抽象到一個服務介面中。應用程式使用基於REST的JSON服務來檢索配置資料,而不是編寫直接訪問服務儲存庫的程式碼(也就是從檔案或使用JDBC從資料庫讀取資料)。
- 集中 – 因為基於雲的應用程式可能會有數百個服務,所以最小化用於儲存配置資訊的不同儲存庫的屬性至關重要。將應用程式配置集中在儘可能少的儲存庫中。
- 穩定 – 因為應用程式的配置資訊與部署的服務完全隔離並集中存放,所以不管採用任何方案實現,至關重要的一點就是保證其高可用和冗餘。
2.2 構建Spring Cloud Config伺服器
為Spring Cloud 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.spring</groupId>
<artifactId>config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>config-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Spring Cloud Config伺服器的引導類
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Spring Cloud Config服務端的application.properties檔案
server.port=9001
spring.application.name=config-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
# 使用本地檔案系統儲存配置檔案
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=file:D:/tmp/properties
# 配置RabbitMQ連線
spring.rabbitmq.host=192.168.3.12
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
management.endpoints.web.exposure.include=bus-refresh
在D:/tmp/properties下面建立properties檔案
檔名稱格式:服務名-profile值.properties
服務名:Spring Cloud Config客戶端中,pring.application.name
的值。
profile值:Spring Cloud Config客戶端中,spring.cloud.config.profile
的值。
2.2 構建Spring Cloud Config客戶端
為Spring Cloud Config客戶端建立pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
Spring Cloud Config客戶端的bootstrap.properties檔案
我們首先要做的是將application.properties
檔案重新命名為bootstrap.properties
,然後再進行一下配置:
pring.application.name=order-eureka
server.port=9101
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.rabbitmq.host=192.168.3.12
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=config-server
spring.cloud.config.profile=dev
2.3 動態更新客戶端配置
開發團隊想要使用Spring Cloud配置伺服器時,遇到的第一個問題是,如何在屬性變化時動態重新整理應用程式。Spring Cloud配置伺服器始終提供最新版本的屬性,通過其底層儲存庫,對屬性進行的更改將是最新的。
但是,Spring Boot應用程式只會在啟動時讀取它們的屬性,因此Spring Cloud配置伺服器中進行的屬性更改不會被Spring Boot應用程式自動獲取。Spring Boot Actuator提供了一個@RefershScope
註解,允許開發團隊訪問/actuator/bus-refresh
,這會強制Spring Boot應用程式重新讀取應用程式配置。
例如,在一下程式碼中userName
的值是通過讀取配置檔案來獲取的。
@RestController
@RefreshScope
public class HelloController {
@Value("${user.name}")
private String userName;
@GetMapping("/hello")
public String hello(){
return userName;
}
}
如果在程式執行中,我們需要動態改變username
的值得話,需要這樣來做:
- 事先在
HelloController
類上新增@RefreshScope
註解 - 修改註解中相應的值
- 用post請求訪問以下URL:
http://<Spring Cloud Config伺服器IP地址>:<Spring Cloud Config伺服器埠號>/actuator/bus-refresh
然後當我們再次訪問/hello
的時候,我們就可以看到修改後的值了。
2.4 配置檔案加密解密
在預設情況下,Spring Cloud Config伺服器在應用程式配置檔案中以純文字格式儲存所有屬性,包括像資料庫憑證這樣的敏感資訊。
將敏感資訊作為純文字儲存在原始碼儲存庫中是一種非常糟糕的做法。Spring Cloud Config可以讓我們輕鬆加密敏感屬性。Spring Cloud Config 支援使用對稱加密(共享金鑰)和非對稱加密(公鑰/私鑰)。
我們將看看如何搭建Spring Cloud Config伺服器以使用對稱加密。要做到這一點,需要:
- 搭建對稱加密環境
- 加密和解密屬性
- 配置微服務以在客戶端使用加密
2.4.1 搭建對稱加密環境
首先,要下載並安裝加密所需的 Oracle JCE jar:
下載網址:jce8-download
然後將獲得的ZIP檔案解壓,並將其中的兩個檔案local_policy.jar
和US_export_policy.jar
,放到[JAVA_HOME]/jre/lib/security/policy/unlimited/
下面並覆蓋原檔案。
然後,建立加密金鑰:
在Spring Cloud Config伺服器中建立bootstrap.properties
配置檔案,並在新增如下配置:
encrypt.key=ThisIsSecretKey #這就是金鑰
最後,我們要測試環境是否搭建成功:
使用GET方法,訪問http://<配置伺服器IP地址>:<配置伺服器埠號>/encrypt/status
,如果Response的結果是:
{
"status": "OK"
}
的話則表示環境搭建成功。
2.4.2 加密和解密測試
加密:
解密:
2.4.3 配置微服務以在客戶端使用加密
首先,需要禁用服務端解密:
在Spring Cloud Config服務端的application.properties
檔案中新增以下配置:
# 禁用服務端解密
spring.cloud.config.server.encrypt.enabled=false
其次,在客戶端新增金鑰:
在Spring Cloud Config客戶端的bootstrap.properties
檔案中新增以下配置:
#金鑰
encrypt.key=ThisIsSecretKey
最後,在客戶端新增依賴:
在Spring Cloud Config客戶端的pom.xml
檔案中新增以下依賴:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
</dependency>
至此,我們便完成了Spring Cloud Config服務端和客戶端的配置。
當我們需要對某一個屬性進行加密&解密的時候,需要在密文前新增{cipher}
字首,如下:
user.name={cipher}d6cc36e2c80706b71fef73fbe6d5b878f57f6a76305ee1bac1722d62f53ce86a
只有這樣,客戶端才能正確分別哪些屬性需要解密。