写在前面

前面我们对Spring Cloud Config分布式配置中心的服务端进行了较为细致的学习,接下来开始学习对于客户端的配置。

URI指定配置中心

在前面我们在config-client项目的bootstrap.properties启动文件中添加了spring.cloud.config.uri=http://localhost:4001/这一参数,用于指定配置中心的地址。那么问题来了,为什么需要通过手动来设置配置中心的地址呢?因为Spring Cloud Config的客户端在启动的时候,默认会从工程的classpath中加载配置信息并启动应用。只有我们配置了spring.cloud.config.uri这一参数的时候,客户端应用才会尝试连接Spring Cloud Config的服务端来获取远程配置信息并初始化Spring环境配置。同时在前面也多次提到,必须将该参数配置在bootstrap.properties启动文件、环境变量或是其他优先级高于应用Jar包内的配置信息中,这样客户端才能正确加载到远程配置。

如果开发者不配置spring.cloud.config.uri这一参数,那么SpringCloud Config的客户端会默认尝试连接http://localhost:8888这一地址。可以打开ConfigClientProperties类,里面有一个uri属性,其默认值就是http://localhost:8888

可以看到这是一个属性配置类,我们之前配置的spring.application.namespring.cloud.config.profile等属性都是用于定位配置信息。

服务化配置中心

在前面我们学习了如何构建服务注册中心、如何发现与注册服务,其实Config Server也是可以以服务的方式注册到服务中心,并被其他的应用所发现进而实现配置信息的获取。接下来就详细介绍如何将Config Server注册到服务中心,并通过服务发现来访问Config Server同时来获取Git仓库中的配置信息。这里分为两步,首先是配置服务端,其次是客户端,相应的步骤如下所示:

服务端配置

第一步,添加依赖。在config-server项目的pom.xml配置文件中添加spring-cloud-starter-netflix-eureka-server依赖,以实现将分布式配置中心加入到Eureka服务治理体系中的目的,如下所示:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

第二步,配置服务注册中心地址。在config-server项目的application.yml配置文件中添加eureka.client.service-url.defaultZone参数来指定服务注册中心的地址,完整的配置信息如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 配置项目名称
spring.application.name=config-server

# Git仓库地址、仓库下的子目录、用户名、密码
spring.cloud.config.server.git.uri=https://github.com/licheetools/springcloud_test.git
spring.cloud.config.server.git.search-paths=config-repo
spring.cloud.config.server.git.username=username
spring.cloud.config.server.git.password=password

# 配置端口号
server.port=4001
# 配置服务注册中心地址
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

第三步,开启服务发现注解。在config-server项目的入口类ConfigServerApplication上添加@EnableDiscoveryClient注解,这样就可以将config-server服务注册到服务注册中心里面:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

第四步,先启动服务注册中心(eureka-server项目),然后再启动config-server项目,然后在浏览器地址栏中输入http://localhost:1111/链接,可以发现在Eureka Server的信息面板中已经看到了config-server服务,这说该服务已经注册成功了:

客户端配置

第五步,添加依赖。回到config-client项目中,在其pom.xml配置文件中添加spring-cloud-starter-netflix-eureka-server依赖,以实现将config-client项目加入到Eureka服务治理体系中的目的,如下所示:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

第六步,配置服务注册中心地址。请注意config-client项目是在其bootstrap.properties启动文件中配置服务注册中心地址,这一点与服务端在配置文件中配置是不同的,且两者不能搞混淆。打开config-client项目的bootstrap.properties启动文件,在里面新增如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
# 配置应用名称
spring.application.name=envy
# 配置端口号
server.port=4002
# 配置服务注册中心地址,用于服务的注册与发现
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
# 开启通过服务来访问Config Server的功能
spring.cloud.config.discovery.enabled=true
# 指定Config Server注册的服务名
spring.cloud.config.discovery.service-id=config-server
# 指定环境,用来定位Git中的资源
spring.cloud.config.profile=prod

第七步,开启服务发现注解。在config-client项目的入口类ConfigClientApplication上添加@EnableDiscoveryClient注解,这样就可以将config-client服务注册到服务注册中心里面:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}

第八步,配置接口来加载Git中的配置信息。可以使用之前定义的HelloController类中的两个接口来测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RefreshScope
public class HelloController {
@Value("${envythink}")
String envythink;

@Autowired
Environment environment;

@GetMapping(value = "/envythinkOne")
public String envythinkOne(){
return this.envythink;
}

@GetMapping(value = "/envythinkTwo")
public String envythinkTwo(){
return environment.getProperty("envythink","未定义");
}
}

第九步,先启动eureka-server服务注册中心,再启动配置中心config-server,最后再启动config-client项目,然后在浏览器地址栏中输入http://localhost:1111/链接,此时可以看到在Eureka Server的信息面板中可以看到该应用已经被注册成功:

之后访问客户端应用提供的服务链接http://localhost:4002/envythinkOne时,它会返回笔者在GitHub仓库中envy-prod.properties配置文件中配置的envythink属性的内容:

失败快速响应

Spring Cloud Config的客户端会预先加载很多其他的信息,然后才开始连接Config Server进行属性的注入,这样可能造成一个问题,尤其是当我们构建的应用较为复杂的时候,可能在连接Config Server之前就已经花费了较长的时间,而我们想要的则是快速知道当前应用是否能顺利的从Config Server中获取到配置信息,这使得开发者在初期构建调试环境时可以减少不必要的等待时间。那么问题来了,如何实现这个功能?

要实现客户端优先判断是否能从Config Server中正常获取数据,并快速响应失败内容,开发者只需在config-client项目的bootstrap.properties启动文件中添加spring.cloud.config.fail-fast=true这一参数即可。

接下来通过一个例子来进行验证,首先注释掉之前添加的spring.cloud.config.fail-fast=true这一参数,然后不启动配置中心(config-server项目),然后直接启动客户端(config-client项目),你会发现客户端在报错之前,已经加载了很多内容。

但是当你添加上述配置,然后再启动客户端(config-client项目,注意依旧不启动服务端),可以看到此时客户端在报错之前几乎不怎么加载内容。也就是说该参数可以有效的避免当Config Server配置有误时,开发者不需要多等待前置的一些加载时间,也就是实现了快速返回失败信息的目的。

失败快速重试

在前面我们举的例子是当Config Server宕机或者是客户端配置不正确而导致启动失败的情况,此时快速响应的配置可以发挥较好的效果。但是如果是因为网络波动或者其他间歇性的原因导致的问题,也认为是连接不成功,那么这种代价似乎有些高,那么针对这种情况该怎样操作呢?

Config客户端还提供了自动重试这一机制,不过在开启重试功能之前,必须确保前面的失败快速响应功能已经开启,即spring.cloud.config.fail-fast=true这一条件成立,之后再进行后面的操作。

第一步,在config-client项目的pom.xml配置文件中添加spring-retryspring-boot-starter-apo依赖:

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步,直接启动客户端应用(config-client项目),注意依旧不启动注册中心。添加完依赖后即可启动config-client项目,然后观察控制台的输出信息,可以看到客户端在连接Config Server失败后,不是立即返回错误信息,而是继续尝试连接,知道第6次尝试还是失败后才返回错误信息:

客户端通过这种重试机制,可以避免一些间歇性问题引起的失败,进而导致客户端应用无法启动的情况。

当然了,如果开发者觉得6次重试比较少,还可以对默认的最大重试次数和重试间隔等进行设置,可以在客户端应用(config-client项目)的bootstrap.properties启动文件中添加如下参数来实现:

1
2
3
4
5
6
7
8
# 设置间隔乘数,默认1.1,也就是当最初的间隔是1000毫秒时,那么下一次失败后的间隔为1100毫秒
spring.cloud.config.retry.multiplier=1.1
# 初始重试间隔时间,默认1000ms
spring.cloud.config.retry.initial-interval=1000
# 设置最大间隔时间,默认2000ms
spring.cloud.config.retry.max-interval=2000
# 设置最大重试次数,默认为6
spring.cloud.config.retry.max-attempts=6

其实这些参数的默认值可以在RetryProperties类中得到验证:

获取远程配置

我们在前面详细介绍服务端配置的时候介绍了{application}{profile}{label}等参数。在Git仓库中,一个形如{application}-{profile}.properties或者{application}-{profile}.yml的配置文件,通过URL请求和客户端配置的访问可以总结如下所示:
(1)通过向Config Server发起GET请求以直接的方式获取,可使用的链接形式:

  • 不带{label}分支时,默认使用master分支,此时可以使用:

    1
    2
    /{application}-{profile}.properties
    /{application}-{profile}.yml
  • {label}分支时,需要在接口中配置分支信息,此时可以使用:

    1
    2
    3
    /{label}/{application}-{profile}.properties
    /{label}/{application}-{profile}.yml
    /{application}/{profile}[/{label}]

    (2)通过客户端配置方式加载的内容:

  • spring.application.name:对应config-server项目中配置的仓库中的配置文件的{application}部分;

  • spring.cloud.config.profile:对应config-server项目中配置的仓库中的配置文件中的{profile}部分;

  • spring.cloud.config.label:对应config-server项目中配置的仓库中对应的分支内容,如果不配置则默认为master。

动态刷新配置

个人认为动态刷新配置这一功能是Spring Cloud Config中最厉害的,有了它开发者可以对一些配置内容进行实时更新。

接下来将通过前面的例子来学习如何将现有项目实现配置内容的实时更新。不过在此之前需要梳理一下现有的架构。截止到目前,我们已经完成了3个部分,其中config-repo是一个定义在GitHub仓库中的一个目录,其中存储了名为envy的多环境配置文件,且该配置文件中有一个envythink参数;config-server是配置中心,它配置了Git仓库的服务端;config-client这是一个以config-server为配置中心的客户端,应用名称为envy,它用来访问配置服务器以获取配置信息。同时该config-client应用中提供了一个/envythinkOne接口,该接口会获取config-repo/envy-prod.properties中的envythink的属性,并予以返回。

在改造之前,需要确保这个/envythinkOne接口是可用的,因此需要将config-server项目和config-client项目都启动起来,然后访问客户端提供的REST接口地址http://localhost:4002/envythinkOne,可以看到页面确实输出了以下信息:

接下来我们尝试使用Git工具来修改GitHub仓库里config-repo/envy-prod.properties文件中envythink的值为prod-111.0,然后再来访问http://localhost:4002/envythinkOne链接,可以看到依旧是之前的prod-1.0,也就是说这样其实没有实现实时更新的。我们还需要在config-client项目中做一些改造以实现配置信息的动态刷新。

第一步,在config-client的pom.xml文件中新增spring-boot-starter-actuator依赖,它是一个监控模块,里面包含了/refresh端点的实现,该端点用于实现客户端应用配置信息的重新获取与刷新:

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

第二步,重启config-client项目,访问一次http://localhost:4002/envythinkOne链接,可以查看当前的配置值为prod-1.0。
第三步,修改GitHub仓库里config-repo/envy-prod.properties文件中envythink的值为prod-111.0,如下所示:

第四步,再次访问http://localhost:4002/envythinkOne链接,可以查看当前的配置值依旧为prod-1.0,配置值没有改变:

第五步,在config-client项目的bootstrap.properties配置文件中配置开启端点的参数,然后重启config-client项目:

1
2
3
4
5
6
# actuator配置
management.endpoints.enabled-by-default=true
management.endpoint.health.show-details=always
#management.endpoints.web.exposure.include=refresh,info,health
management.endpoints.web.exposure.include=*
management.endpoints.web.base-path=/actuator

第六步,使用POSTMAN等测试工具通过POST请求发送到http://localhost:4002/refresh链接,可以看到返回内容如下:

第七步,再次访问http://localhost:4002/envythinkOne链接,可以查看当前的配置值已经变成了prod-111.0,说明动态配置动态更新已经实现了:

其实该功能还可以同Git仓库的Web Hook功能进行关联,这样当Git提交变化时,就会给对于的配置主机发送/refresh请求来实现配置信息的实时更新。

但是当我们的系统发展壮大之后,维护这样的刷新清单其实也是一个较大的负担,且很容易出错,那么有什么办法可以解决这个复杂度呢?这就涉及到后面要学习的Spring Cloud Bus组件了,它可以实现以消息总线的方式来进行配置变更的通知,并完成集群上的批量配置与更新。

OK,那么到此关于SpringCloud Config分布式配置中心客户端的详解就到此结束,后续进入消息总线Spring Cloud Bus组件的学习。