老司机都喜欢在程序设计中尽可能的把各种参数做成可配置的,等到产品需求发生改动的时候,优雅的修改一行配置,重新加载一次配置,就满足了需求。
配置(Configuration)是不修改代码的情况下,对程序的运行调整的能力。
简单来讲,配置可以分成两类:
程序的配置一般而言,分为几个环境:
这几个环境的配置是有差异的,所以配置管理功能应当能够根据当前环境读取对应的配置。
前面提到的不修改代码,只是最低要求,相当多的情况下,程序是不能停机的,这就提出了热更新的需求。
另外,配置的修改应该是有记录可追溯的。
Spring 实现了非常友好的配置读取方式,
Spring Boot 程序默认使用 application.properties 进行配置。
例如一个参数:
flag=0
在程序中可以通过:
@Value("${flag}) int flag;
来读取。
也可以通过设置多个配置文件:
application.properties application-dev.properties application-test.properties application-prod.properties
application-dev.properties
flag=1
application-test.properties
flag=2
application-prod.properties
flag=3
和在 application.properties 中声明当前的活跃的 profile:
spring.profiles.active=dev
来实现根据运行环境切换配置信息。
以上只是 Spring 强大的配置能力的冰山一角,有兴趣可以参考 外部配置文档 。
其实以上描述的 Spring Boot 的配置能力已经很强了,但是有个很致命的问题——无法热更新。
如果非得实现热更新,那可以把配置做成 JSON 文件,再实现一个 endpoint,重新读取一次配置。
假设有一千个实例呢?
项目 github: https://github.com/spring-cloud/spring-cloud-config
简介:External configuration (server and client) for Spring Cloud
这个项目包含两部分:
负责从 git/svn 等版本管理系统中读取配置,并以 http 的方式提供服务。
根据客户端配置,从指定的 Server 中读取对应配置,并且与 Spring 本身的 PropertyResource 和 Environment 无缝结合。同时,提供了统一的方式进行配置热更新。
对于这个系统的基础配置和运行,可以参阅下面两个 url:
https://spring.io/guides/gs/centralized-configuration/
http://blog.didispace.com/spring-cloud-starter-dalston-3/
Spring Cloud Config 实现了非常优雅的热更新。
@Configuration @RefreshScope public class ConsumeConfig { @Value("${consume.desc}") private String consumeDesc; @Value("#{'${api.server.round.card}'.split(',')}") private List<String> cardList; }
在配置这个注解之后,调用客户端的 /refresh endpoint,这个 Bean 就会刷新,同时个 Bean 的依赖方下次方法调用时也会更新 Bean 引用。
有些时候,你需要监听这个更新事件,把拿到的最新配置,重新初始化一些部件。那你可以添加:
@EventListener(EnvironmentChangeEvent.class)
这个 annotation。
@Component public class ConsumeService { @Autowired ConsumeConfig consumeConfig; @EventListener(EnvironmentChangeEvent.class) void onEnvChange() { //do re-init stuff } }
Config Server 任何人都可以通过 http 访问配置,这个不大好,建议加上认证,最简单是使用 Spring-Security 添加一个 basic authentication。
build.gradle 添加依赖:
dependencies { compile('org.springframework.boot:spring-boot-starter-security') }
application.properties 添加密码(用户名默认是 user):
security.user.password=xxx
重启服务器,再次通过 http 访问配置的时候需要验证。
服务器端做了验证,那客户端也需要添加相应的配置:
bootstrap.yml
spring: application: name: application cloud: config: uri: http://yourhost.com profile: dev username: user password: xxx
很多时候,为了避免 application.properties 过于臃肿,你可能需要把一些配置文件拆出来,例如专门负责邀请奖励的配置:
invite.properties invite-dev.properties invite-test.properties invite-prod.properties
同样,也区分了多个环境。
那在使用 Spring Cloud 的时候如何读取这个文件呢?
spring: application: name: application,invite
注意上面的 application.name 是以逗号分隔的两组配置名称。
我在实际使用中发现,在事件监听函数中,使用更新后的配置的时候:
@Value("${consume.desc}") private String consumeDesc; @Value("#{'${api.server.round.card}'.split(',')}") private List<String> cardList;
第一个配置是使用 @Value 绑定一个字符串类型,第二个配置是使用 @Value 中的 SpEL 去将配置中的字符串,切割成 List。
我发现第二个,无法切割成功。试验了多次,还没有找到答案。我已经在 Spring Cloud Config 的 github 上提交了 issue 。
如果有了解这个的,请不吝赐教。
在不做二次开发的情况下,这个配置中心的数据是对所有的 client 开放的。某些情况下,这种设定并不合适。
Spring Cloud Config 本身是可以通过 http 来提供 JSON 文件的访问的,但是Spring Boot 原生并不支持 JSON 配置的读取和解析。
其实配置管理工具,选择还是不少的,下面列举一下。
配置文件管理,可以热更新,跟 Spring 没有绑定。
专注管理配置文件,也可以实现热更,跟 Spring 没有绑定。
携程开源的,功能很全
百度的一位工程师的开源项目,功能也很全
来自阿里的开源,有些日子了
http://blog.didispace.com/spring-cloud-starter-dalston-3/
http://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html
http://jm.taobao.org/2016/09/28/an-article-about-config-center/
https://blog.coding.net/blog/spring-cloud-config
本文由kvh 创作,采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。