项目中一般都会有一些配置文件,例如数据库连接配置、redis连接信息等。在传统的项目中,通常将配置文件与源码放在一起打包部署到服务器上。但是在微服务架构中,,采用这种方式就会存在一些问题。为了保证服务的高可用,通常针对每一个服务都会启动多个实例,提供服务,通过负载均衡机制来将请求转发到多个实例中的一个上。如果需要修改配置文件,就需要将这个服务的所有的实例的配置文件都进行修改,一般每个实例都会部署在不同的机器上,这样挨个去修改配置文件会及其繁琐。springcloud提供了配置中心可以简化这个操作。我们可以将配置文件存储到远端仓库中(一般使用git或者svn作为远端仓库),然后同一个微服务的多个实例都从远端仓库读取同一份配置文件,这样在修改时,也只需要修改一次就可以了。
下面来介绍一下基于git来实现的配置中心。
服务端
准备工作:
先创建如下三个配置文件,然后在GitHub上创建一个仓库,将创建的配置文件上传的仓库。
这三个配置文件中的内容分别如下:
config-dev.properties:
hello=dev
desc=this is a dev config
config-pro.properties:
hello=pro
config-test.properties:
hello=test
创建一个springboot作为配置中心服务端,引入以下pom依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
在该项目的启动类上添加@EnableConfigServer
注解,就可以将该项目作为一个服务中心服务。
修改application.properties文件,添加以下配置:
spring.application.name=config-server
server.port=7001
# git配置信息
# 配置git仓库位置
spring.cloud.config.server.git.uri=https://github.com/xxxxgithub/springcloud-config-demo
# 配置仓库路径下的相对搜索位置,可以配置多个,英文逗号分隔
spring.cloud.config.server.git.searchPaths=
# 访问git仓库的用户名
spring.cloud.config.server.git.username=git仓库用户名
# 访问git仓库的用户名密码
spring.cloud.config.server.git.password=git仓库密码
然后启动项目,测试一下在server端是否能够读取到GitHub仓库中的配置文件的内容,在浏览器地址栏直接访问http://localhost:7001/config/dev,可以看到如下信息:
{
"name": "config",
"profiles": ["dev"],
"label": null,
"version": "82feae0a7c71b7e95e0a71e7b21b9f19d8254a79",
"state": null,
"propertySources": [{
"name": "https://github.com/xxxxgithub/springcloud-config-demo/config-dev.properties",
"source": {
"hello": "dev"
}
}]
}
这些信息包含了配置文件的位置、名称、版本以及具体内容等。至此说明在服务端已经能够读取到GitHub仓库中的配置文件了。
从上面访问的那个URL中可以看出,spring对我们创建的配置文件的文件名进行了解析,然后生成了那个URL,那么spring是按照什么规则解析文件名并且拼装成URL的呢?规则如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
上面URL中的各个占位符会映射到{application}-{profile}.properties对应的配置文件,{label}对应的则是git仓库的分支,默认缺省是master分支。
在config-dev.properties配置文件中新增一行:
desc=this is a dev config
将改动push到git仓库,再次访问http://localhost:7001/config/dev :
{
"name": "config",
"profiles": ["dev"],
"label": null,
"version": "b95700ff5ee3adabd2d952ab543088bae7c2379f",
"state": null,
"propertySources": [{
"name": "https://github.com/xxxxgithub/springcloud-config-demo/config-dev.properties",
"source": {
"hello": "dev",
"desc": "this is a dev config"
}
}]
}
可以看到,server端已经能从git仓库中读取到最新的配置文件的内容了。
在客户端获取配置文件内容
创建一个新的springboot项目作为客户端去从server端获取配置文件的内容。
在项目的pom文件中增加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
创建bootstrap.properties文件:
spring.application.name=config
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:7001/
spring.cloud.config.label=master
# spring.cloud.config.discovery.service-id= 指定配置中心的service-id,便于扩展为高可用配置集群
server.port=9005
注意:上面说过,springcloud-config会按照{application}-{profile}.properties的格式去解析配置文件的文件名。这里的spring.application.name配置值一定要与配置文件的{application}部分保持一致。
另外还需要注意一下bootstrap.properties与application.properties文件的加载顺序和优先级。
项目启动时会先加载bootstrap.properties文件。
创建一个controller,模拟在client中获取配置:
@RestController
public class SpringCloudConfigClientController {
@Value("${hello}")
private String hello;
@Value("${desc}")
private String description;
@RequestMapping("hello-config")
public String helloConfig() {
return hello + ":" + description;
}
}
client的启动类使用默认生成的就好,不需要改变,如下:
@SpringBootApplication
public class SpringcloudConfigclientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConfigclientApplication.class, args);
}
}
先启动server,然后启动client,访问:http://localhost:9005/hello-config,就可以看到页面上显示了配置信息:
refresh
接着上一节,我们继续来做个测试,首先在本地将config-dev.properties文件中的配置修改一下,如下:
hello=dev
desc=this is a dev config update aaaa
然后将修改push到git仓库,访问config-server:http://localhost:7001/config/dev,页面上的响应内容如下:
{
"name": "config",
"profiles": ["dev"],
"label": null,
"version": "0d87394cd708673c6e947f252a368eae5a1cc266",
"state": null,
"propertySources": [{
"name": "https://github.com/xxxxgithub/springcloud-config-demo/config-dev.properties",
"source": {
"hello": "dev",
"desc": "this is a dev config update aaaa"
}
}]
}
可以看到,配置中心读取到的配置文件内容已经是修改过的,说明配置中心可以实时读取到仓库中最新的配置信息,接着我们访问以下config-client:http://localhost:9005/hello-config,页面显示如下:
发现客户端却不能读取到修改后的文件内容,为什么呢?
spring cloud config 分为服务端和客户端,服务端的作用是将git中存储的配置文件发布为TEST接口,客户端通过调用服务端的REST接口获取配置,但是客户端并不能主动感知到配置文件的变化,从而去获取新的配置信息。针对这一问题,spring cloud config 提供了解决方案,每个客户单通过POST方法触发各自的/refresh。
修改客户端的pom,增加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在客户端的controller类上增加@RefreshScope
注解:
@RefreshScope
@RestController
public class SpringCloudConfigClientController {
@Value("${hello}")
private String hello;
@Value("${desc}")
private String description;
@RequestMapping("hello-config")
public String helloConfig() {
return hello + ":" + description;
}
}
在客户端的bootstrap.properties文件中增加以下配置:
management.endpoints.web.exposure.include=refresh
注意这里springboot1.x跟springboot2.x不一样。
然后启动客户端,使用postman发送POST请求:http://localhost:9005/actuator/refresh,得到如下响应信息:
[
"config.client.version",
"desc"
]
然后再到浏览器中发送http://localhost:9005/hello-config请求,发现页面上已经是修改过后的配置信息了:
至此,我们已经搭建起了一个配置中心,并且能够在客户端拿到实时更新的配置信息了。
**注意:**配置中心也可以做高可用,启动多个实例并注册到服务注册中心。本文只是单独介绍配置中心server和配置中心client的用法,并没有将server注册到服务在注册中心,如果需要可自行查阅配置方法。
最后本文还遗留一个问题,每次修改git仓库的配置文件后都要手动发一个POST请求refresh,有点麻烦,能不能有一种方法可以在git仓库中的配置文件变化后,自动发送refresh请求呢?
这里可以利用GitHub网站的的webhook功能,这里先不介绍,留着以后再说。