Spring Cloud 配置中心

闲话配置

老司机都喜欢在程序设计中尽可能的把各种参数做成可配置的,等到产品需求发生改动的时候,优雅的修改一行配置,重新加载一次配置,就满足了需求。

配置(Configuration)是不修改代码的情况下,对程序的运行调整的能力。

简单来讲,配置可以分成两类:

  1. 系统配置:包括线程池大小、数据库连接等,变化频率较低
  2. 业务配置:功能开关,功能参数等,变化较为频繁

程序的配置一般而言,分为几个环境:

  1. 开发环境
  2. 测试环境
  3. 生产环境

这几个环境的配置是有差异的,所以配置管理功能应当能够根据当前环境读取对应的配置。

前面提到的不修改代码,只是最低要求,相当多的情况下,程序是不能停机的,这就提出了热更新的需求。

另外,配置的修改应该是有记录可追溯的。

Spring Boot 的配置管理

Spring 实现了非常友好的配置读取方式,

Spring Boot 程序默认使用 application.properties 进行配置。

例如一个参数:

1
flag=0

在程序中可以通过:

1
2
@Value("${flag})
int flag;

来读取。

也可以通过设置多个配置文件:

1
2
3
4
application.properties
application-dev.properties
application-test.properties
application-prod.properties

application-dev.properties

1
flag=1

application-test.properties

1
flag=2

application-prod.properties

1
flag=3

和在 application.properties 中声明当前的活跃的 profile:

1
spring.profiles.active=dev

来实现根据运行环境切换配置信息。

以上只是 Spring 强大的配置能力的冰山一角,有兴趣可以参考外部配置文档

这还不够

其实以上描述的 Spring Boot 的配置能力已经很强了,但是有个很致命的问题——无法热更新。

如果非得实现热更新,那可以把配置做成 JSON 文件,再实现一个 endpoint,重新读取一次配置。

假设有一千个实例呢?

Spring Cloud Config

项目 github:https://github.com/spring-cloud/spring-cloud-config

简介:External configuration (server and client) for Spring Cloud

这个项目包含两部分:

  • Server

负责从 git/svn 等版本管理系统中读取配置,并以 http 的方式提供服务。

  • Client

根据客户端配置,从指定的 Server 中读取对应配置,并且与 Spring 本身的 PropertyResource 和 Environment 无缝结合。同时,提供了统一的方式进行配置热更新。

对于这个系统的基础配置和运行,可以参阅下面两个 url:

https://spring.io/guides/gs/centralized-configuration/

http://blog.didispace.com/spring-cloud-starter-dalston-3/

实操进阶

热更新

Spring Cloud Config 实现了非常优雅的热更新。

  • 客户端的配置 Bean 添加 @RefreshScope
1
2
3
4
5
6
7
8
9
10
@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。

1
2
3
4
5
6
7
8
9
10
11
@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 添加依赖:

1
2
3
dependencies {
compile('org.springframework.boot:spring-boot-starter-security')
}

application.properties 添加密码(用户名默认是 user):

1
security.user.password=xxx

重启服务器,再次通过 http 访问配置的时候需要验证。

  • 客户端配置

服务器端做了验证,那客户端也需要添加相应的配置:

bootstrap.yml

1
2
3
4
5
6
7
8
9
spring:
application:
name: application
cloud:
config:
uri: http://yourhost.com
profile: dev
username: user
password: xxx

加载多组配置文件

很多时候,为了避免 application.properties 过于臃肿,你可能需要把一些配置文件拆出来,例如专门负责邀请奖励的配置:

1
2
3
4
invite.properties
invite-dev.properties
invite-test.properties
invite-prod.properties

同样,也区分了多个环境。

那在使用 Spring Cloud 的时候如何读取这个文件呢?

1
2
3
spring:
application:
name: application,invite

注意上面的 application.name 是以逗号分隔的两组配置名称。

一些问题

@EventListener 与 SpEL

我在实际使用中发现,在事件监听函数中,使用更新后的配置的时候:

1
2
3
4
5
@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

如果有了解这个的,请不吝赐教。

update 2017-10-26 * 我在 github 上提交了 issue: https://github.com/spring-cloud/spring-cloud-config/issues/821 得到回复:We have only ever documented that placeholders ${} work, not SpEL.

权限管理

在不做二次开发的情况下,这个配置中心的数据是对所有的 client 开放的。某些情况下,这种设定并不合适。

JSON 文件的读取和解析

Spring Cloud Config 本身是可以通过 http 来提供 JSON 文件的访问的,但是Spring Boot 原生并不支持 JSON 配置的读取和解析。

相关选择

其实配置管理工具,选择还是不少的,下面列举一下。

  • owner http://owner.aeonbits.org/

配置文件管理,可以热更新,跟 Spring 没有绑定。

  • cfg4j http://www.cfg4j.org/

专注管理配置文件,也可以实现热更,跟 Spring 没有绑定。

  • applolo https://github.com/ctripcorp/apollo

携程开源的,功能很全

  • disconf https://github.com/knightliao/disconf

百度的一位工程师的开源项目,功能也很全

  • diamond https://github.com/takeseem/diamond

来自阿里的开源,有些日子了

参考文献

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

相关文章推荐