Spring Cloud Config
1.开发中的实际问题
随着线上项目变的日益庞大,每个项目都散落着各种配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。
市面上开源的配置中心有很多,BAT每家都出过,360的QConf、淘宝的diamond、百度的disconf都是解决这类问题。国外也有很多开源的配置中心Apache的Apache Commons Configuration、owner、cfg4j等等。这些开源的软件以及解决方案都很优秀,但是Spring Cloud Config,因为它功能全面强大,可以无缝的和spring体系相结合,够方便够简单颜值高。
2.什么是 spring cloud config
在我们了解spring cloud config之前,我们可以想想一个配置中心提供的核心功能应该有什么
- 提供服务端和客户端支持
- 集中管理各环境的配置文件
- 配置文件修改之后,可以快速的生效
- 可以进行版本管理
- 支持大的并发查询
- 支持各种语言
Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件,默认情况下使用git。
3.本地环境配置
3.1 server端配置
3.1.1 创建项目,引入pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
3.1.2 配置application.yml
erver:
port: 8006
spring:
application:
name: spring-cloud-config-server
profiles:
active: native # 配置使用本地储存
cloud:
config:
server:
native:
search-locations: classpath:properties/ # 搜索src/main/resource 下的propertie 文件夹下的文件
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8000/eureka/
3.1.3 在resources 下创建文件夹
neo-config-dev.properties
#Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=960721
neo-config-test.properties
#Redisspring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=1
#DBConfiguration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=960721
neo-config-pro.properties
#Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=2
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=59852369
3.1.4 启动类配置
启动类添加@EnableConfigServer
,激活对配置中心的支持
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class,args);
}
}
3.1.5 测试
http://localhost:8006/neo-config/dev
3.2 client端配置
3.2.1 pom文件引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 配置使用redis启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.2.2 配置文件
3.2.2.1 配置bootstrap.properties
spring.cloud.config.name=neo-config
spring.cloud.config.profile=test
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=spring-cloud-config-server
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
spring.application.name:对应{application}部分
spring.cloud.config.profile:对应{profile}部分
spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用
spring.cloud.config.uri:配置中心的具体地址
spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。
特别注意:上面这些与spring-cloud相关的属性必须配置在bootstrap.properties中,config部分内容才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也是先于application.properties。
3.2.2.2 配置application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/sys?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=59852369
mybatis.mapper-locations=classpath:mapper/*.xml
3.2.3 启动测试
4.GIT配置环境
首先在github上面创建了一个文件夹config-repo用来存放配置文件,为了模拟生产环境,我们创建以下三个配置文件:
// 开发环境neo-config-dev.properties
// 测试环境neo-config-test.properties
// 生产环境neo-config-pro.properties
每个配置文件中都写一个属性neo.hello,属性值分别是 hello im dev/test/pro 。下面我们开始配置server端.
https://github.com/miaohangbo/config-repo.git
4.1server 端
4.1.1、添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
只需要加入spring-cloud-config-server包引用既可。
4.1.2.配置
server:
port: 8006
spring:
application:
name: spring-cloud-config-server
cloud:
config:
server:
git:
uri: https://github.com/miaohangbo/config-repo.git/ # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
username: miaohangbo # git仓库的账号
password: aini59852369 # git仓库的密码
Spring Cloud Config也提供本地存储配置的方式。我们只需要设置属性spring.profiles.active=native,Config Server会默认从应用的src/main/resource目录下检索配置文件。也可以通过spring.cloud.config.server.native.searchLocations=file:E:/properties/属性来指定配置文件的位置。虽然Spring Cloud Config提供了这样的功能,但是为了支持更好的管理内容和版本控制的功能,还是推荐使用git的方式。
4.1.3.启动类
启动类添加@EnableConfigServer
,激活对配置中心的支持
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
到此server端相关配置已经完成
4.1.5.测试
首先我们先要测试server端是否可以读取到github上面的配置信息,直接访问:http://localhost:8006/neo-config/dev
返回信息如下
{"name":"neo-config","profiles":["dev"],"label":null,"version":"389ffdb255f823237b5647d89fe0cead5c4ab7f9","state":null,"propertySources":[{"name":"https://github.com/miaohangbo/config-repo//neo-config-dev.properties","source":{"neo.hello":"hello im dev"}}]}
上述的返回的信息包含了配置文件的位置、版本、配置文件的名称以及配置文件中的具体内容,说明server端已经成功获取了git仓库的配置信息。
如果直接查看配置文件中的配置信息可访问:config-dev.properties,返回:neo.hello: hello im dev
修改配置文件neo-config-dev.properties中配置信息为:neo.hello=hello im dev update
,再次在浏览器访问http://localhost:8006/neo-config-dev.properties
,返回:neo.hello: hello im dev update
。说明server端会自动读取最新提交的内容
仓库中的配置文件会被转换成web接口,访问可以参照以下的规则:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
以neo-config-dev.properties为例子,它的application是neo-config,profile是dev。client会根据填写的参数来选择读取对应的配置。
4.2.client 端
主要展示如何在业务项目中去获取server端的配置信息
4.2.1 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
引入spring-boot-starter-web包方便web测试
4.2.2 配置文件
需要配置两个配置文件,application.properties和bootstrap.properties
application.properties如下:
spring.application.name=spring-cloud-config-clientserver.port=8002
bootstrap.properties如下:
spring.cloud.config.name=neo-config
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=spring-cloud-config-server
eureka.client.serviceUrl.defaultZone= http://localhost:8000/eureka/
4.2.3 web测试
使用@Value
注解来获取server端参数的值
@RestController
class HelloController {
@Value("${neo.hello}")
private String hello;
@RequestMapping("/hello")
public String from() {
return this.hello;
}
}
启动项目后访问:http://localhost:8003/hello,返回:hello im dev update说明已经正确的从server端获取到了参数。到此一个完整的服务端提供配置服务,客户端获取配置参数的例子就完成了。
我们在进行一些小实验,手动修改neo-config-dev.properties中配置信息为:neo.hello=hello im dev update1提交到github,再次在浏览器访问http://localhost:8003/hello,返回:neo.hello: hello im dev update,说明获取的信息还是旧的参数,这是为什么呢?因为springboot项目只有在启动的时候才会获取配置文件的值,修改github信息后,client端并没有在次去获取,所以导致这个问题。
4.3. refresh 刷新
Spring Cloud Config分服务端和客户端,服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置。客户端如何去主动获取新的配置信息呢,springcloud已经给我们提供了解决方案,每个客户端通过POST方法触发各自的
/refresh
修改spring-cloud-config-client
项目已到达可以refresh的功能。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加了spring-boot-starter-actuator
包,spring-boot-starter-actuator
是一套监控的功能,可以监控程序在运行时状态,其中就包括/refresh
的功能。
4.3.1、 开启更新机制
需要给加载变量的类上面加载@RefreshScope
,在客户端执行/refresh
的时候就会更新此类下面的变量值。
@RestController
@RefreshScope // 使用该注解的类,会在接到SpringCloud配置中心配置刷新的时候,自动将新的配置更新到该类对应的字段中。
class HelloController {
@Value("${neo.hello}")
private String hello;
@RequestMapping("/hello")
public String from() {
return this.hello;
}
}
4.3.2、测试
Spring boot 2.0的改动较大,/bus/refresh全部整合到actuador里面了,所以之前1.x的management.security.enabled全部失效,不适用于2.0
适用于2.0的配置是这样的:
management.endpoints.web.exposure.include=refresh
OK 这样就改造完了,以post请求的方式来访问http://localhost:8003/actuator/refresh
就会更新修改后的配置文件。
我们再次来测试,首先访问http://localhost:8003/hello
,返回:hello im dev
,我将库中的值修改为hello im dev update
。在win上面打开cmd执行curl -X POST http://localhost:8003/actuator/refresh
,返回["neo.hello"]
说明已经更新了neo.hello
的值。我们再次访问http://localhost:8003/hello
,返回:hello im dev update
,客户端已经得到了最新的值。
5.高可用环境搭建
客户端都是直接调用配置中心的server端来获取配置文件信息。这样就存在了一个问题,客户端和服务端的耦合性太高,如果server端要做集群,客户端只能通过原始的方式来路由,server端改变IP地址的时候,客户端也需要修改配置,不符合springcloud服务治理的理念。springcloud提供了这样的解决方案,我们只需要将server端当做一个服务注册到eureka中,client端去eureka中去获取配置中心server端的服务既可。
5.1 server端改造
5.1.1 pom文件改造
<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>
</dependencies>
需要多引入spring-cloud-starter-netflix-eureka-client
包,来添加对eureka的支持。
5.1.2 、配置文件改造
server:
port: 8001
spring:
application:
name: spring-cloud-config-server
cloud:
config:
server:
git:
uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
username: username # git仓库的账号
password: password # git仓库的密码
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8000/eureka/ ## 注册中心eurka地址
增加了eureka注册中心的配置
5.1.3、启动类
启动类添加@EnableDiscoveryClient
激活对配置中心的支持
/**
* Created by 54110 on 2019-07-01.
*/
@EnableConfigServer
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
这样server端的改造就完成了。先启动eureka注册中心,在启动server端,在浏览器中访问:http://localhost:8000/
就会看到server端已经注册了到注册中心了。
5.2 客户端改造
5.2.1 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
需要多引入spring-cloud-starter-netflix-eureka-client
包,来添加对eureka的支持。
5.2.2 修改bootstrap.properties配置文件
spring.cloud.config.name=neo-config
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=spring-cloud-config-server
eureka.client.serviceUrl.defaultZone=http://admin:123456@localhost:8000/eureka/
主要是去掉了spring.cloud.config.uri
直接指向server端地址的配置,增加了最后的三个配置:
spring.cloud.config.discovery.enabled
:开启Config服务发现支持spring.cloud.config.discovery.serviceId
:指定server端的name,也就是server端spring.application.name
的值eureka.client.serviceUrl.defaultZone
:指向配置中心的地址
这三个配置文件都需要放到bootstrap.properties
的配置中
5.2.3 启动类改造
启动类添加@EnableDiscoveryClient
激活对配置中心的支持
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
启动client端,在浏览器中访问:http://localhost:8000/
就会看到server端和client端都已经注册了到注册中心了。
5.3高可用
为了模拟生产集群环境,我们改动server端的端口为8003,再启动一个server端来做服务的负载,提供高可用的server端支持
如上图就可发现会有两个server端同时提供配置中心的服务,防止某一台down掉之后影响整个系统的使用。
我们先单独测试服务端,分别访问:http://localhost:8006/neo-config/dev
、http://localhost:8009/neo-config/dev
返回信息:
{"name":"neo-config","profiles":["dev"],"label":null,"version":"6eecd82c8cbbab7d1fc167a5b4e543cdede44cd1","state":null,"propertySources":[{"name":"https://github.com/miaohangbo/config-repo//neo-config-dev.properties","source":{"neo.hello":"hello im dev11123232"}}]}
说明两个server端都正常读取到了配置信息。
再次访问:http://localhost:8002/hello
,返回:hello im dev update
。说明客户端已经读取到了server端的内容,我们随机停掉一台server端的服务,再次访问http://localhost:8002/hello
,返回:hello im dev update
,说明达到了高可用的目的。