1.前言
Spring Cloud Config为分布式系统中的外部化配置提供服务器端和客户端支持。使用Config Server,您可以在中心位置管理所有环境中应用程序的外部属性。客户端和服务器上的概念与Spring Enviroment和PropertySource抽象,因此它们非常适合Spring应用程序,而且可以与以任何语言运行的任何应用程序一起使用。在应用程序从开发人员到测试人员再到生产人员的整个部署过程中,您可以管理这些环境之间的配置,并确保应用程序具有它们迁移时所需的一切。服务器存储后端的默认实现使用git,因此它轻松支持带标签的配置环境版本,并且可以通过各种工具来访问这些内容来管理内容。添加替代实现并将其插入Spring配置很容易。
Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Cloud 项目,通过简单的配置即可实现功能。
目前有一些用的比较多的开源的配置中心,比如携程的 Apollo、蚂蚁金服的 disconf 等,对比 Spring Cloud Config,这些配置中心功能更加强大。有兴趣的可以拿来试一试。
2.实现最简单的配置中心
最简单的配置中心,就是启动一个服务作为服务方,之后各个需要获取配置的服务作为客户端来这个服务方获取配置。
2.1 在GitHub上创建一个配置中心仓库
目录结构如下:
配置文件大致如下:
server:
port: 9090
eurekaServer:
port: 6662
feign:
client:
provide:
name: configtest-feign-local
2.2 创建配置中心服务端
新建一个springboot项目,引入config-server 和 starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.2 配置Config
bootstrap.yml:
spring:
application:
name: cloud-config-server
cloud:
config:
server:
git:
uri: 你配置中心仓库的GitHub地址
username: yourUserName
password: yourPassword
default-label: master #配置文件分支
search-paths: cloud-config-client #配置文件所在根目录
2.3在 Application 启动类上增加相关注解 @EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class CloudConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(CloudConfigServerApplication.class, args);
}
}
2.4启动服务,测试
从springcloud官网 :https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.2.RELEASE/reference/html/
可以看到,springcloud Config 有一套它的访问规则:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
我们在浏览器中可以直接看的,但是为了美观,我用postman试了一下:
其他几种规则我也试过了,都是可以的,在这里我就不一一截图了。
3.实现配置中心客户端,使用配置中心服务端
配置中心服务端好了,配置数据准备好了,接下来,就要在我们的项目中使用它了。
3.1新建新的springboot项目
引入starter-web和starter-config依赖,跟服务端一样。
3.2初始化配置文件
bootstrap.yml:
spring:
application:
name: cloud-config-client
cloud:
config:
uri: http://localhost:8760
label: master
profile: local
application.yml:
server:
port: 9999
spring:
profiles:
active: local
eurekaServer:
port: localPort
feign:
client:
provide:
name: localName
3.3增加测试接口
@Component
@Data
public class GitConfig {
@Value("${eurekaServer.port}")
public String port;
@Value("${feign.client.provide.name}")
public String providerName;
}
@RestController
@RequestMapping("/config")
public class ConfigController {
@Resource
private GitConfig gitConfig;
@GetMapping("/test01")
public String test01() {
String result = gitConfig.port + " : " + gitConfig.providerName;
System.out.println(result);
return result;
}
}
3.4测试
启动项目,测试:
可以看到,返回的数据是从配置中心仓库里的数据,测试通过!
Spring Cloud Config 在项目启动时加载配置内容这一机制,导致了它存在一个缺陷,修改配置文件内容后,不会自动刷新。例如我们上面的项目,当服务已经启动的时候,去修改 github 上的配置文件内容,这时候,再次刷新页面,对不起,还是旧的配置内容,新内容不会主动刷新过来。
但是,总不能每次修改了配置后重启服务吧。如果是那样的话,还是不要用它了为好,直接用本地配置文件岂不是更快。
它提供了一个刷新机制,但是需要我们主动触发。那就是 @RefreshScope 注解并结合 actuator ,注意要引入 spring-boot-starter-actuator 包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在 config client 端配置中增加 actuator 配置:
management:
endpoints:
web:
exposure:
include: '*'
enabled-by-default: true
metrics:
tags:
application: ${spring.application.name}
environment: ${spring.profiles}
在需要读取配置的类上增加 @RefreshScope 注解:
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Resource
private GitConfig gitConfig;
@GetMapping("/test01")
public String test01() {
String result = gitConfig.port + " : " + gitConfig.providerName;
System.out.println(result);
return result;
}
}
重启client端后修改GitHub上的配置文件,再来使用postman测试,发现并没有更改,查阅资料发现是因为我使用了@Value,如果换成@ConfigurationProperties注解就咩问题了,至于为什么,有时间再整理个文章,嘿嘿:
其实自动刷配置的方式有很多,如Webhooks、springcloud Bus等,有兴趣的话大家可以了解一下。
5.结合Eureka实现springcloud Config
以上讲了 Spring Cloud Config 最基础的用法,但是如果我们的系统中使用了 Eureka 作为服务注册发现中心,那么 Spring Cloud Config 也应该注册到 Eureka 之上,方便其他服务消费者使用,并且可以注册多个配置中心服务端,以实现高可用。
那么我们下面就会将springcloud Config集成到Eureka。
5.1创建eureka注册中心
简单讲一下如何新建一个eureka注册中心,
引入相关依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
启动类加上@EnableEurekaServer注解,
配置:
server:
port: 8761
eureka:
instance:
hostname: localhost
appname: registrationCenter
client:
register-with-eureka: false # 单点的时候设置为 false 禁止注册自身
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
5.2将配置中心服务端注册到eureka
添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类增加注解:@EnableEurekaClient。
增加eureka配置:
eureka:
server:
max-time-for-replication: 30000
client:
register-with-eureka: true #将此项目注册到Eureka服务
service-url:
defaultZone: http://localhost:8761/eureka/ #注册地址
fetch-registry: true
5.2将配置中心客户端注册到eureka
跟服务端一样,添加依赖,添加注解,增加eureka配置;
除此之外,还要配置与服务端的关系,这里我们不再使用config.uri了,而是使用service-id属性:
spring:
application:
name: cloud-config-client
cloud:
config:
label: master
profile: local
discovery:
enabled: true
service-id: cloud-config-server
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
5.3启动项目测试:
可以看到我们的服务端和客户端都注册上啦。
接下来在postman上试一下:
也是OK的!
这里有几点是需要大家注意的:
- 配置中心仓库内配置文件所在文件夹要跟服务端配置的search-paths一致,这里后面再研究下怎么优化成动态的匹配;
- 在继承eureka的时候,关于eureka和git config的配置要放在bootstrap.yml 中,否则会请求默认的 config server 配置,这是因为当你加了配置中心,服务就要先去配置中心获取配置,而这个时候,application.yml 配置文件还没有开始加载,而 bootstrap.yml 是最先加载的。
另外,在这里记录一下我的作死经历,因为在本地启动微服务项目的时候,每次都要起很多服务,Eureka注册中心啊、ConfigServer啊,所以觉得很麻烦,于是我把这两个服务放到了我的腾讯云服务器上,然后在本地启动ConfigClient,于是问题出现了:
连接不上,这是为什么呢?仔细一看,这个地址是我服务器内网的地址,并不是外网的地址,再去eureka上看了一眼,果然是。不通是正常的。。暂时还没有想到有啥办法将eureka上注册的地址改成外网地址,也没想到办法在我本地将内网地址映射成外网地址,如果是域名的话,倒是可以通过修改hosts文件达到我们的目的。所以我只能暂时只使用服务器上的注册中心,ConfigServer只能在本地起了,事实证明遮掩是可行的。
其实如果我同时起了本地和服务器上的Configserver的话,它第一次连接服务器端的Configserver超时了会继续重试,下一次就会试着连接我本地的Configserver,就会成功了。
这是我eureka上已经注册的服务:
看一下效果:
延伸阅读:
这在里,我们项目读取的配置文件路径是写死的,不能做动态的匹配,这篇文章会告诉大家如何优化: