一、前言

随着业务的扩展,为了方便开发和维护项目,我们通常会将大项目拆分成多个小项目做成微服务,每个微服务都会有各自配置文件,管理和修改文件起来也会变得繁琐。而且,当我们需要修改正在运行的项目的配置时,通常需要重启项目后配置才能生效。

上述的问题将是本篇需要解决的问题。

二、介绍

2.1 简单介绍

Spring Cloud Config 用于为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端和客户端两部分。服务端(config server)也称为分布式配置中心,是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息,加密/解密信息等访问接口。而客户端(config client)则是微服务架构中各微服务应用或基础设施,通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。

2.2 运行原理

如上图,当 Config Client 首次启动时会向 Config Server 获取配置信息,Config Server 接收到请求再从远程私有仓库获取配置(连接不上项目会报错),并保存到本地仓库中。

当 Config Client 再次启动时会向 Config Server 获取配置信息,Config Server 还是会先从远程私有仓库拉去数据。如果网络问题或认证问题导致无法连接远程私有库,Config Server 才会从本地仓库获取配置信息返回给 Config Client。

三、Config 实战

本次实战基于 Eureka 篇的项目进行扩展演练。不清楚的读者请先转移至 《Spring Cloud Eureka服务治理》 进行浏览。

我们使用配置中心来维护 order-server 的配置数据(application.yml)。

测试场景:由于配置中心服务本身也是一个微服务,因此我们需要将配置中心注册到 Eureka 上,当 order-server 启动时先向 Eureka 获取配置中心的访问地址,然后从配置中心获取相应的配置信息进行正常启动。

本篇实战用到的项目列表:

项目服务实例实例id端口描述
eureka-serverEureka-Servereureka-server19001注册中心(Eureka 服务端)
eureka-serverEureka-Servereureka-server29002注册中心(Eureka 服务端)
config-serverConfig-Serverconfig-server8500配置中心(Eureka 客户端、Config 服务端)
config-clientConfig-Clientconfig-client10001配置客户端(Eureka 客户端、Config 客户端)

3.1 新建私有仓库

在 GitHub 上新建一个私有仓库,名为 cloud-config。
我们将 order-server 项目的配置文件放到改仓库中,如下图:

新建 3 个 yml 文件,内容为:
config-client-dev.yml

1
2
3
4
5
6
7
server:
port: 10001
eureka:
instance:
instance-id: config-client
prefer-ip-address: off # 访问路径可以显示 IP
env: dev

config-client-pro.yml

1
2
3
4
5
6
7
server:
port: 10001
eureka:
instance:
instance-id: config-client
prefer-ip-address: off # 访问路径可以显示 IP
env: pro

config-client-test.yml

1
2
3
4
5
6
7
server:
port: 10001
eureka:
instance:
instance-id: config-client
prefer-ip-address: off # 访问路径可以显示 IP
env: test

3.2 config 服务端

新建一个 spring boot 项目,名为 config-server(任意名字)。

3.2.1 添加依赖:

1
2
3
4
5
6
7
8
9
        <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>

3.2.2 application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server:
port: 8500

spring:
application:
name: Config-Server
cloud:
config:
server:
git:
uri: https://github.com/wno704/cloud-config.git #配置了Git仓库的地址,这里用的是码云,当然你也可以使用别的Git代码托管平台;
username: (您的帐号)
password: (您的密码)
basedir: E:\workspace\workspace(idea)\spring\cloud\config-server\src\main\data # 本地库目录
label: master

eureka:
instance:
instance-id: config-server
prefer-ip-address: off
client:
register-with-eureka: true # 是否向注册中心注册自己
fetch-registry: true # 是否检索服务
service-url:
#defaultZone: http://127.0.0.1:8761/eureka/
defaultZone: http://eureka01:9001/eureka/,http://eureka02:9002/eureka/

3.2.3 启动类添加 @EnableConfigServer

1
2
3
4
5
6
7
8
9
10
@EnableConfigServer
@EnableEurekaClient
@SpringBootApplication
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}

}

启动成功后,我们在idea的http工具中访问 http://localhost:8500/config-client-test.yml


config-server 服务成功拉去远程私有仓库的配置数据。

其中,访问规则如下:

1
2
<IP:PORT>/{name}-{profiles}.yml
<IP:PORT>/{label}/{name}-{profiles}.yml

name:文件名,可当作服务名称

profiles: 环境,如:dev,test,pro

lable: 分支,指定访问某分支下的配置文件,默认拉去 master 分支。

3.3 config 客户端

在 config-client 项目中。

3.3.1 添加依赖:

1
2
3
4
5
6
7
8
9
        <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>

3.3.2 新建 bootstrap.yml

删除 application.yml,并新建 bootstrap.yml,保存如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
application:
name: Config-Client
cloud:
config:
discovery:
enabled: true
service-id: Config-Server
profile: dev
label: master

eureka:
instance:
instance-id: config-client
prefer-ip-address: off
client:
register-with-eureka: true # 是否向注册中心注册自己
fetch-registry: true # 是否检索服务
service-url:
defaultZone: http://eureka01:9001/eureka/,http://eureka02:9002/eureka/

配置中,通过 spring.cloud.config.discovery.service-id 确定配置中心,再通过 spring.application.name 的值拼接 spring.cloud.config.profile 的值,从而确定需要拉去从配置中心获取的配置文件。(如:config-client-dev)

注意:必须保留 eureka 注册中心的配置,否则 config-client 无法连接注册中心,也就无法获取配置中心(config-server)的访问信息。

3.3.3 测试

新建一个测试类:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/test")
public class TestController {

@Value("${env}")
private String env; // 从配置中心获取

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

启动成功后,我们在idea的http工具中访问 http://localhost:10001/test/getConfigInfo

成功获取 config-server 从远程私有仓库拉去的数据,由于在 bootstrap.yml 中配置了 spring.cloud.config.profile=dev,因此拉取到的数据就是 config-client-dev.yml 中的数据。

四、Config-Server额外配置

上面我们简单的地搭建了一个Config-Server,下面我们来进一步了解Config-Server的可用配置。

4.1 占位符的使用

在Config-Server中,除了固定配置一个Git仓库地址外,我们也可以使用占位符来灵活的指定Git仓库地址。

将上面Config-Server的Git仓库地址改为:

1
2
3
4
5
6
spring:
cloud:
config:
server:
git:
uri: https://github.com/wno704/{application}

这里使用占位符{application}来代替上面的cloud-config,通过这种配置,我们可以让不同的Config-Client去不同的Git仓库获取配置。比如,当Config-Client的项目名称为order的时候,对应Git仓库地址为: https://github.com/wno704/order ,当名称为test时,对应Git仓库地址为: https://github.com/wno704/test 。这样我们就可以为不同的项目配置不同的Git仓库。

4.2 子目录支持

除了使用占位符为每个项目创建单独的Git仓库来存储配置信息外,我们也可以只创建一个Git仓库来存储配置,只不过是将不同的项目配置放置到不同的目录下,只需要像下面这样配置即可:

1
2
3
4
5
6
7
8
9
spring:
cloud:
config:
server:
git:
uri: https://github.com/wno704/cloud-config/
username: xxxx
password: xxxx
search-paths: '{application}'

通过上面的配置,我们可以实现在 https://github.com/wno704/cloud-config/ 仓库中,一个Config-Client对应一个配置目录的效果,即当Config-Client的项目名称为order的时候,其搜索配置的目录为 https://github.com/wno704/cloud-config/order

注意:这里search-paths的占位符必须加上单引号'',否则没办法正确读取配置。

4.3 clone-on-start

默认情况下Config-Server在启动的时候并不会马上就去Git参考clone配置文件,只有当Config-Clinet从Config-Server获取相关配置信息的时候,其才会去进行clone操作。我们可以将clone-on-start属性设置为true,其Config-Server在启动的时候就进行clone操作:

1
2
3
4
5
6
spring:
cloud:
config:
server:
git:
clone-on-start: true

这样做的好处在于,当Git连接信息有误时,可以马上发现。

4.4 整合Spring Security

Config-Server中包含了Git连接信息,为了使其更加安全,我们可以通过Spring Security来做用户名密码认证。

在Config-Server中加入Spring Security依赖:

1
2
3
4
        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后在Config-Server的配置文件application.yml中加入用户名和密码:

1
2
3
4
5
spring:
security:
user:
name: wno704
password: 19920503

与此同时,我们也需要在Config-Client中配置Config-Server的用户名和密码,否则在获取配置的时候将报401错误:

1
2
3
4
5
spring:
cloud:
config:
username: wno704
password: 19920503

五、Config-Client额外配置

5.1 刷新配置

在Config-Server和Config-Client都启动后,如果这时候Git仓库存储的配置信息改变了,在不重启Config-Client的情况下,配置信息是不会跟着更新的。那么如何在Git仓库存储的配置得到改变的时候也刷新Config-Client中获取到的配置值呢?很简单,我们只需要对Config-Client进行简单的改造:

5.1.1 增加依赖

在Config-Client中添加spring-boot-starter-actuator依赖:

1
2
3
4
        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

该依赖包含了/refresh端点,可以用来刷新配置。

5.1.2 加入@RefreshScope

然后在获取配置的Controller上加入@RefreshScope注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RefreshScope
@RestController
@RequestMapping("/test")
public class TestController {

@Value("${env}")
private String env; // 从配置中心获取

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

5.1.3 关闭认证

值得注意的是,我们需要在Config-Client的配置文件中加入如下配置来关闭认证,否则我们无权访问/refresh端点

1
2
3
4
5
management:
endpoints:
web:
exposure:
include: refresh

springboot2.0.3.RELEASE版本以下配置如下:

1
2
3
management:
security:
enabled: false

5.1.4 验证

重启Config-Client,访问 http://localhost:10001/test/getConfigInfo

这时候我们将Git仓库中对应的配置信息改为“测试配置刷新”,再次访问 http://localhost:10001/test/getConfigInfo 会发现值还是"dev"。

git配置webhooks,URL为 http://localhost:10001/refresh

接着我们使用以下命令来刷新配置:

curl -X POST http://localhost:10001/actuator/refresh

这时候再次访问 http://localhost:10001/test/getConfigInfo

可看到在不重启Config-Client的前提下配置值已经得到了更新。