目录
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
只有这样,客户端才能正确分别哪些属性需要解密。