面临的问题
在分布式微服务架构系统中,业务和系统功能被拆分成了几十甚至上百个服务实例。每个服务实例就是以往单体应用时代的一个独立部署的工程。每个工程都需要自己独立的启动加载和运行时配置文件。
在项目开发的过程中,我们不可避免的会涉及到配置文件的修改,例如调整一下数据库的IP地址,修改某个功能的启用开关状态等等。如果系统结构中的微服务节点较少,那么常规的代码+配置的开发方式足以解决问题。
当系统逐步迭代,其微服务会越来越复杂,慢慢演化成网状依赖结构,这个时候常规的代码+配置的开发方式就并不合适了,因为还要考虑整体系统的扩展性、伸缩性和耦合性等。这些问题中,配置的管理也是非常麻烦的。
解决方案
工程化带来的问题需要用工程化的方案来解决。为了方便的解决配置复杂繁琐的问题,我们在微服务架构系统中引入配置中心(Spring Cloud Config)。通过它来统一管理微服务架构系统中的配置文件内容修改与分发,配置自动同步的问题,为项目的部署与运维提供便利。
Spring Cloud Config采用集中式管理每个微服务的配置信息,并使用GIT等版本仓库统一存储配置内容,实现版本化管理控制。微服务与配置中心使用REST方式交互来实现可扩展的配置服务。
Spring Cloud Config配置中心解决了微服务系统的配置中心化、配置版本控制、平台独立、语言独立等问题,其特性如下:
- 提供服务端和客户端支持(Spring Cloud Config Server和Spring Cloud Config Client);
- 集中式管理分布式环境中的配置信息;
- 基于spring环境提供配置管理,与Spring系列框架无缝结合;
- 可用于任何语言开发环境;
- 默认基于GIT仓库实现版本控制;
如何做到
配置中心(Config Server)本身作为一个微服务注册到服务注册中心中(通常可以是Eureka,Consul,Dubbo等提供的注册中心服务),配置中心会根据spring.cloud.config.server.git.uri来找到配置数据(它可以是git存储库的位置,也可以是本地文件),配置了正确的uri之后,Config Server就可以从远程Git服务拉取资源配置。
在项目中,基本上所有的基础微服务都是Config Client,它们都通过Config Server做外部配置集中管理和动态环境切换。每个Client会在启动时通过Server来拉取相应的配置资源信息,同时还会通过消息总线(Spring Cloud Bus),以发布-订阅的方法监听在运行时由Server端动态发布的配置变更信息。
当我们有配置资源的变更需要时,通过GIT或SVN,将配置资源信息提交到资源仓库中。提交后触发对应的Hook调用Server来拉取最新的资源。如此便实现了:资源 -> GIT/SVN -> 配置中心Config Server -> 消息总线Bus -> Config Client 的整个链条动态变更。
如何使用
基于spring cloud 2.x版本Spring Cloud Config主要为系统中的服务实例提供外部配置,这些配置通常是可变的,默认是使用git存放配置信息。Spring Cloud Config分为服务端和客户端两种角色,服务端用于统一管理配置中心并为客户端提供配置信息,客户端用于指定配置中心并在服务启动时向配置中心拉取资源初始化本地服务,客户端的配置通常是不变的。这样就能做到配置与交付分离,当项目部署到不同环境时,不需要去修改客户端的配置文件,只需要指定其运行配置中心中的哪种环境。
Spring Cloud Config的服务端也是一个服务实例,需要导入服务端依赖。
<!--Spring Cloud Config 服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
特别强调的是,如果使用svn进行代码管理,还需要导入指定svn的依赖,如果使用git则不用。
<!-- svn依赖 -->
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
</dependency>
在服务端的配置文件中,需要指定配置文件的存放地址,如果使用svn,需要指定spring.profiles.active: subversion,如果不指定项目启动则会报错。
要想实现服务端的高可用,可以将服务端注册到服务中心,当客户端指定配置中心时从服务中心获取即可。
服务端的搭建已经介绍完,下面是客户端是如何与服务端建立关系的。
在客户端中也需要导入指定依赖
<!-- Spring Cloud Config 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
注意,客户端的配置文件使用bootstrap.yml,其加载优先级高于application,yml,所以项目启动时会预先加载bootstrap.yml中的内容,并且这些内容通常是不可变的,比如指定配置中心,监控配置,快速失败响应等配置可以放在bootstrap.yml文件中。
到此,客户端已经能够通过服务端去加载配置文件,但是大家更关心的是,如果配置文件修改了,怎么及时的获取最新的配置信息,毕竟每次如果都需要重启的话成本太大,接下来我们介绍Spring Cloud Config如何实现配置文件的自动刷新机制。
修改客户端pom.xml文件,添加如下依赖
<!--Spring Boot Actuator 监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--spring-cloud-bus 消息总线-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
在bootstrap.yml中添加actuator 监控配置:
启用监控management.endpoints.enabled-by-default=true
暴露刷新接口
management.endpoints.web.exposure.include='*'(用*可以包含全部端点)
在bootstrap.yml配置rabbitmq的地址以及用户密码:
spring.rabbitmq.host=xxx
spring.rabbitmq.port=xxx
spring.rabbitmq.username=xxx
spring.rabbitmq.password=xxx
除了以上的修改,注意还要在需要实现自动刷新的类上添加@RefreshScope注解。
接着解释一下为什么要做这样的修改,这些新加的配置是如何相互合作实现动态刷新的,首先actuator是用于感知及监测服务器端的变化,在不启用消息总线之前其与@RefreshScope结合能够实现单个端口的刷新,即调用/refresh接口实现,这种属于手动刷新。显然这种刷新方式并不是最优的,分布式系统中实例那么多,我们不能每一个服务实例都调用一遍接口,我们希望达到的效果是调用一次即可所有实例都生效,所以引入消息总线spring cloud bus,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,当有一个客户端触发了配置更新事件(注意这时调用/bus-refresh接口),即会向总线传达这个消息,总线接到消息并通知给其它客户端,其它客户端接收到通知,请求Server端获取最新配置,至此所有客户端都得到最新配置。
到此为止,配置中心的动态刷新机制还差一步,就是上述我们需要手动去触发一个客户端调用/bus-refresh接口,这个动作可以使用我们的代码版本管理系统来实现,当配置文件有更新时自动触发接口,可以与github的webhook进行配合,svn也有类似的hook机制。
总结
关于Spring Cloud Config的使用就为大家介绍到这里,其实Spring Cloud Config还能实现的功能有很多,主要看系统自身需求进行配置,这里我们为大家介绍的是服务搭建及配置资源的动态刷新,这也是运用Spring Cloud Config最核心要解决的问题,想了解关于Spring Cloud Config的更多内容大家可以去网上查找资源,但是要注意的是目前网上的许多教程都是spring cloud 1.5.x版本的,使用spring cloud 2.x需要注意版本升级带来的改动。