Consul 是一个集配置管理、服务注册和发现于一体的微服务基础组件,它提供的这些功能我们在微服务开发中都用到了。在实践中,我们发现 Spring Cloud 没有提供在启动时自动加载某一个配置文件到 Consul 配置中心的功能,于是参照 Consul 社区的一个开源项目 cfg4j-pusher 进行了二次开发,实现了 Spring Boot 应用启动时自动加载指定配置文件到 Consul 配置中心的功能。以下是软件环境:
在这里我新建一个工程来说明实现过程,整个工程的结构如下:
整个工程依照 Spring Boot 工程的目录结构创建,在 com.consul.pusher 包下有四个类,其中 ConsulApplication 的作用就不用说了,它是整个服务的启动类。剩下的三个类说明如下:
ConsulConfig :提供加载配置到 Consul 中的操作。它定义了一个私有的 init 方法,这个方法被 @PostConstruct 注解所标记,主要作用是在 ConsulConfig 依赖注入完成之后读取指定配置文件,将文件里的配置信息推送到 Consul 配置中心中。该方法在整个应用生命周期中只执行一次,定义为私有的主要是不允许外部调用,保证安全性
ConsulService 和 ConsulServiceImpl : ConsulServiceImpl 实现了 ConsulService 接口,主要提供 Consul 配置中心业务逻辑操作,它主要封装了 Consul java client 的一些方法。
resources 目录下的 bootstrap.yml 和 application.yml 分别为微服务全局配置文件和业务信息配置文件。在服务启动期间, application.yml 配置文件的内容会被 ConsulConfig 类的 init 方法读取并推送到 Consul 配置中心。在这里我们没有将 bootstrap.yml 的文件内容推送到 Consul 配置中心,主要是考虑到:
bootstrap.yml 里定义的配置信息不经常修改
Spring Boot 天生不支持动态修改数据库、 ES 连接信息。 bootstrap.yml 里的定义的数据库连接、 ES 连接等信息通过 Consul 配置中心修改后不能生效,还需要重启应用才能生效。并且,需要修改数据库连接、 ES 连接信息的场景的大部分是因为当前服务不可用产生的,服务的高可用不应由 Consul 来维护
经过与团队成员讨论,开发配置自动推送功能的初衷有两个:
方便 devops 团队进行自动化运维。微服务部署过程中,配置的管理和修改是一件让人头疼的事情,无论是开发和运维,都希望减少手动修改配置的次数,通过自动推送配置功能,可以轻松解决这个问题。 Consul 官方虽然提供了 HTTP API 供使用者加载指定配置到 Consul 中,但是需要开发给运维提供配置文件,再由运维手动执行命令加载这些配置文件到 Consul 中,不仅容易出错,管理也不便
无论是分布式服务还是微服务,都应该有一套工具化、通用化的公共服务类库。服务开发中,有很多重复的、可以封装成工具类的代码,我们可以构建一个公共服务类库,将这些代码组织起来,供大家使用。这样既解决了代码重复的问题,也使服务更加健壮和高可用。业内优秀的例子比如 Apache commons 、Google guava 系列类库
基于第二点初衷,产品发布了几版之后,我将自动推送配置的代码抽取了出来,封装成一个公共服务类库。封装之后,工程目录结构如下:
因为公共服务类库不是一个可运行的微服务应用,所以需要去除 ConsulApplication 类,基础库单元测试通过之后,就可以推送到我们的 maven 私服中。接下来,就是使用这个基础库了,使用步骤如下:
Consul 依赖: <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-Consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-Consul-config</artifactId>
</dependency>
将自定义的配置统一提取到 application.yml 里。注意: yml 文件里如果有空的 value 值,请用单引号 '' 代替,否则 yml 解析会报错
在 pom 文件里引入 consul-pusher 公共服务类库, consul-pusher 类库的 Consul 配置加载类在服务启动时自动加载配置到 Consul 中:
<dependency> <groupId>com.Consul.pusher</groupId> <artifactId>Consul-pusher</artifactId> <version>1.0</version> </dependency>
Application 启动类上,加入 @ComponentScan(value = "com.Consul.pusher") ,使公共类库相关的类被 Spring Boot 扫描到,比如: @SpringBootApplication
@EnableDiscoveryClient
@EnableScheduling // 启用定时调度功能,Consul需要使用此功能来监控配置改变
@ComponentScan(value = "com.Consul.pusher")
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class);
}
}
使用 @Value 注入配置,在类添加 @RefreshScope 实现业务参数的热更新:
@RefreshScope
@Service
public class StockMarketServiceImpl implements StockMarketService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Value(value = "${kline.host}")
private String kLineHost;
}
至此,整个工程的重构和使用完成。引入 consul-pusher 的服务启动后,在 Consul 配置中心创建的配置信息截图如下,以 consul-pusher 服务本身为例:
源码链接: consul-pusher
Consul 整合参考链接:
Spring Cloud(二)Consul 服务治理实现
快递中心的spring cloud实践
consul配置中心使用指南