spring cloud config 统一配置中心 读取Git/SVN/本地文件配置及动态刷新
一、简介
Spring Cloud Config为分布式系统中的外部化配置提供服务器和客户端支持。使用配置服务器,你可以在中心位置管理所有环境中应用程序的外部属性。服务器存储后端的默认实现使用Git,同时也支持SVN及本地化配置,因此它很容易支持配置环境的标记版本,并且可以被用于管理内容的各种工具访问。可以很容易地添加替代实现,并将它们插入到Spring配置中。 在spring cloud config 组件中,分两个角色,一是config server,二是config client。
一个配置中心提供的核心功能
- 提供服务端和客户端支持
- 集中管理各环境的配置文件
- 配置文件修改之后,可以快速的生效
- 可以进行版本管理
- 支持大的并发查询
- 支持各种语言
Spring Cloud Config可以完美的支持以上所有的需求
二、构建Config Server
创建一个spring-boot项目,取名为config-server,pom.xml中引入依赖:
[code]<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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--bus消息总线动态刷新配置 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> </dependencies>
启动类开启配置服务@EnableConfigServer
[code]import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class,args); } }
application.properties:
[code]#服务端口 server.port=8091 #服务名称 spring.application.name=configServer #服务注册中心 #eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ #-------------------------------------------------------------------------------- #服务的git仓库地址 #spring.cloud.config.server.git.uri=https://github.com/huyilong1995/bdms-service.git #配置文件拉去到本地的目录位置 #spring.cloud.config.server.git.basedir=target/config #配置文件所在的目录 #spring.cloud.config.server.git.search-paths=/** #git仓库的用户名 #spring.cloud.config.username= #git仓库的密码 #spring.cloud.config.password= #-------------------------------------------------------------------------------- #本地化配置file/classpath spring.profiles.active=native spring.cloud.config.server.native.search-locations=file:/C:/Users/13163/Desktop/config #spring.cloud.config.server.native.search-locations=classpath:/config #-------------------------------------------------------------------------------- #使用svn作为配置仓库,必须显示声明profiles.active=subversion #spring.profiles.active=subversion #spring.cloud.config.server.svn.uri=https://192.168.9.56/svn/Config_Files/ #spring.cloud.config.server.svn.username= #spring.cloud.config.server.svn.password= #spring.cloud.config.server.svn.search-paths=/** #spring.cloud.config.server.svn.default-label=config #-------------------------------------------------------------------------------- #rabbitmq配置,用于配置的动态刷新 spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest #是否开启基本的鉴权,默认为true security.basic.enabled=false management.security.enabled=false
这里分别配置了Git、SVN、本地化配置(磁盘或项目根路径)作为配置仓库的相关配置信息,本例仅演示本地化配置
启动程序:访问http://localhost:8091/serviceA-test.properties
证明配置服务中心可以从远程程序获取配置信息
http请求地址和资源文件映射如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
三、构建一个config client
创建一个springboot项目,取名为config-client,其pom文件引入依赖:
[code] <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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
其配置文件bootstrap.properties:
[code]#注意客户端里是没有配置服务的端口的,我们会从仓库中加载application.properties,那里配置了端口 spring.application.name=configClient #对应前配置文件中的{application}部分,这样会加载多个配置文件,注意的是不同的配置文件里有相同的key会造成属性覆盖 spring.cloud.config.name=application,serviceA #对应前配置文件中的{profile}部分 spring.cloud.config.enabled=true spring.cloud.config.profile=test #配置仓库的分支 #spring.cloud.config.label=config1 #配置中心服务端的地址 spring.cloud.config.uri=http://localhost:8091/ #rabbitmq配置 spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest #启动失败时能够快速响应 spring.cloud.config.fail-fast=true
1、注意客户端里是没有配置服务的端口的,我们会从仓库中加载application.properties,那里配置了端口
2、可以这样spring.cloud.config.name=application,serviceA 加载多个配置文件,注意的是不同的配置文件里有相同的key会造成属性覆盖,后面会覆盖前面的配置
3、spring.cloud.config.label=config1 指定了配置仓库的分支,比如Git的默认分支伟master,这里是分文件夹管理配置
写一个rest接口,返回从配置中心读取的变量的值,代码如下:
[code]package com.dscomm.client.config.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.dscomm.client.service.TestService; @RefreshScope @RestController public class TestController { @Value("${name}") private String name; @Value("${age}") private String age; @Value("${sex}") private String sex; @RequestMapping("age") public String age() { return age; } @RequestMapping("sex") public String sex() { return sex; } @RequestMapping("name") public String name() { return name; } }
需要注意的是@RefreshScope 注解,在我们需要刷新的配置类上加上这个注解,就可以实现动态刷新的效果了
启动客户端进行测试:
可以看到客户端运行在了8081端口上,而我们的客户端配置文件里是没有指定端口的,是因为我们在配置仓库的config1分支里面配置了application-test.properties指定了port,客户端应用了此配置。
接下来访问 localhost:8081/sexzhehe
这就说明我们就拿到了serviceA-test.properties里定义的sex的值
关于属性覆盖:可以看到我们在application-test.properties和serviceA-test.properties里都定义了name的值,但是我们在访问localhost:8081/name 会拿到什么呢
这就说明application里的那么被serviceA覆盖掉了,所以应避免多个配置文件配置相同名称的属性
关于动态刷新:以上的配置我们其实就已经实现了动态刷新的功能了。在config的服务端我们引入了bus-amqp的依赖同时配置文件里配置了rabbitMQ的配置信息(需要提前安装rabbitMQ)。启动config服务端可以看到会暴露很多接口信息:
1、资源文件的映射:
2、 刷新配置的接口
当配置仓库的配置文件发生更该,无需重启客户端,只需要调用服务端的bus/refresh接口再配合@refreshScope注解即可
测试:我们修改之前的sex的属性值为man
调用服务端 http://localhost:8091/bus/refresh 接口刷新配置,不重启客户端再次调用客户端的接口,可以看到拿到的属性值已经改变了
严格意义上来说,进行到这一步只能说是半自动刷新,SVN/本地化配置与Git不相同的是,Git可以配置webhook,当git端配置发生改变,自动调用/bus/refresh接口刷新配置,可以达到真正的自动化配置。
拓展:使用SVN和本地化配置达到像Git一样的自动化配置,无需手动调用/bus/refresh接口
思路:可以利用commons-io,服务端一启动就开始监听配置仓库的文件,文件发生变化就自动发送一个post请求调用bus/refresh接口,达到自动刷新的效果
服务端改造:
添加依赖:
[code] <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.5</version> </dependency>
服务启动开始监听:
[code]import java.io.File; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.monitor.FileAlterationMonitor; import org.apache.commons.io.monitor.FileAlterationObserver; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import com.dscomm.service.config.listener.ConfigurationListener; @Component public class DsServiceInit implements ApplicationRunner { @Value("${spring.cloud.config.server.native.search-locations}") private String monitorPath; @Override public void run(ApplicationArguments args) throws Exception { //截取要监听的仓库路径 String path = StringUtils.substring(monitorPath, 6); File dir = new File(path); FileAlterationMonitor monitor = new FileAlterationMonitor(); IOFileFilter filter = FileFilterUtils.or(FileFilterUtils.directoryFileFilter(), FileFilterUtils.fileFileFilter()); FileAlterationObserver observer = new FileAlterationObserver(dir, filter); observer.addListener(new ConfigurationListener(dir)); monitor.addObserver(observer); try { monitor.start(); System.out.println("启动配置文件监听……"); } catch (Exception e) { e.printStackTrace(); } } }
配置文件监听
[code]import java.io.File; import javax.annotation.PostConstruct; import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.dscomm.service.config.service.ConfigService; @Component public class ConfigurationListener extends FileAlterationListenerAdaptor { private static ConfigurationListener configurationListener; @Autowired private ConfigService configService; @PostConstruct public void init() { configurationListener = this; configurationListener.configService = this.configService; } public File DirContext; public ConfigurationListener() { super(); } public ConfigurationListener(File dirContext) { super(); DirContext = dirContext; } @Override public void onDirectoryCreate(File directory) { configurationListener.configService.refreshConfig(); } @Override public void onDirectoryChange(File directory) { configurationListener.configService.refreshConfig(); } @Override public void onDirectoryDelete(File directory) { configurationListener.configService.refreshConfig(); } @Override public void onFileCreate(File file) { configurationListener.configService.refreshConfig(); } @Override public void onFileChange(File file) { configurationListener.configService.refreshConfig(); } @Override public void onFileDelete(File file) { configurationListener.configService.refreshConfig(); } }
发生post请求刷新配置服务
[code]/* * Description:配置刷新服务 * * * History: * ======================================== * Date Version Memo * 2019-3-13 1.0 Created by huyilong * ======================================== * * Copyright 2010-2019, 迪爱斯信息技术股份有限公司保留。 */ package com.dscomm.service.config.service; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class ConfigService { //这里是自己定义的服务端的ip地址 @Value("${service.config.ip}") private String serverIP; @Value("${server.port}") private String port; public void refreshConfig() { CloseableHttpClient httpClient = getHttpClient(); try { String url = "http://" + serverIP + ":" + port + "/bus/refresh"; HttpPost post = new HttpPost(url); post.setHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); post.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); System.out.println("send POST 请求...." + post.getURI()); CloseableHttpResponse httpResponse = httpClient.execute(post); try { org.apache.http.HttpEntity entity = httpResponse.getEntity(); if (null != entity) { System.out.println("-------------------------------------------------------"); System.out.println(EntityUtils.toString(entity, "UTF-8")); } } finally { httpResponse.close(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { closeHttpClient(httpClient); } catch (Exception e) { e.printStackTrace(); } } } public static CloseableHttpClient getHttpClient() { return HttpClients.createDefault(); } public static void closeHttpClient(CloseableHttpClient client) throws IOException { if (client != null) { client.close(); } } }
这样就完成了配置仓库的监听以及自动刷新的功能,当有配置文件发生变化,服务端会自动发生post请求刷新配置而不需要我们再去手动刷新了
修改一下配置文件可以看到
这样客户端的就可以拿到最新的配置信息了
总结:
本例是以本地化配置来使用测试spring cloud config,Git/SVN/本地化配置三种方式的主要区别还是在服务端的配置文件里,三种配置在上面的配置文件里都是经过测试的,可以根据选择合适的仓库配置
- springcloud实战之14动态刷新分布式配置中心属性配置(config)
- Spring Cloud Config 之svn配置仓库与动态刷新(客户端手动手动刷新)
- Spring Cloud Config + Spring Cloud Bus 实现配置文件动态刷新(版本 Finchley.RC1)
- Spring Cloud Config 之svn配置仓库与动态刷新(客户端)
- (Spring Cloud微服务实战-书中之坑)spring cloud config优先读取本地配置,其次读取git仓库
- SpringCloud之分布式配置中心-本地配置文件(Spring Cloud Config) |第十章 -yellowcong
- spring cloud config 之svn仓库配置与动态刷新(服务端)
- Spring Cloud Config 之svn配置仓库与动态刷新(客户端自动刷新)
- spring cloud :统一管理配置文件config
- spring cloud config server 使用本地配置文件报错
- spring cloud config 如何优先读取本地配置
- SpringCloud Config Server使用SVN Hooks实现动态刷新配置
- Spring Cloud Config - 统一配置中心
- spring-cloud-config + spring-cloud-bus实现动态刷新集群配置
- 学习微服务的统一配置管理-springCloud config+git
- spring cloud bus与spring cloud config整合实现应用配置动态刷新
- Spring Cloud Config集成Gitlab(统一配置中心)
- spring cloud config client不能读取到服务器配置文件
- 【SpringCloud】搭建高可用分布式配置中心(Spring Cloud Config)(二)全过程详解(自动刷新)解决webhooks 400错误
- Spring Cloud Config 使用本地配置文件