为了避免参数变化引起的频繁的程序改动,通常我们在应用程序中将常用的一些系统参数、启动参数、数据库参数等等写到配置文件或其他的存储介质里面。
传统应用程序的配置分散,导致了在进行部署、运维方面,需要极大的成本。那么传统的应用程序配置面临哪些问题? 问题一:应用程序多实例集群部署,每个微小的配置的修改将导致每个实例都需要重新打包部署

问题二:每一套环境的配置不同,难于维护,增加了人工犯错的几率

问题三:没有严格的配置管理权限控制,导致公司的核心数据泄露 不知道大家有没有看过一条报道,国外某著名的公司,在开源代码的数据库连接配置中,携带了其"生产环境"的数据库配置信息,导致其核心的用户数据泄露。
除了上面的三点,还有很多传统的配置管理方式面临的问题,所以我们要进行集中的统一的配置管理。这点在微服务应用中体现的更为明显。
比起“分布式配置中心”这个词,我更喜欢集中的配置管理中心叫做“统一配置管理中心”。“分布式”修饰的是应用程序,而“统一”才是修饰配置管理中心的关键词。但是大家都叫分布式配置管理中心,我也就从了大家。但是在理解上要区分过来。
理想的配置管理中心应该是:

配置中心将配置从应用中剥离出来,统一管理,优雅的解决了配置的动态变更、持久化、运维成本等问题。应用自身既不需要去添加管理配置接口,也不需要自己去实现配置的持久化,更不需要引入“定时任务”以便降低运维成本。总得来说,配置中心就是一种统一管理各种应用配置的基础服务组件。
在系统架构中,配置中心是整个微服务基础架构体系中的一个组件,如下图,它的功能看上去并不起眼,无非就是配置的管理和存取,但它是整个微服务架构中不可或缺的一环。
目前市面上用的比较多的配置中心有:
2014年9月开源,Spring Cloud 生态组件,可以和Spring Cloud体系无缝整合。
https://github.com/spring-cloud/spring-cloud-config
2016年5月,携程开源的配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
https://github.com/ctripcorp/apollo
2018年6月,阿里开源的配置中心,也可以做DNS和RPC的服务发现。
https://github.com/alibaba/nacos
对比项目/配置中心 | spring cloud config | apollo | nacos(重点) |
|---|---|---|---|
开源时间 | 2014.9 | 2016.5 | 2018.6 |
配置实时推送 | 弱支持(Spring Cloud Bus) | 支持(HTTP长轮询1s内) | 支持(HTTP长轮询1s内) |
版本管理 | 支持(Git) | 自动管理 | 自动管理 |
配置回滚 | 弱支持(Git+Bus) | 支持 | 支持 |
配置的灰度发布 | 理念上支持,可操作性不强 | 支持 | 1.1.0开始支持 |
权限管理 | 不支持(没有区分用户、角色、权限的概念) | 支持 | 1.2.0开始支持 |
多集群多环境 | 对集群概念支持较弱 | 支持 | 支持 |
多语言 | 只支持Java | Go,C++,Python,Java,.net,OpenAPI | Python,Java,Nodejs,OpenAPI |
分布式高可用最小集群数量 | Config-Server2+Git+MQ | Config2+Admin3+Portal*2+Mysql=8 | Nacos*3+MySql=4 |
配置格式校验 | 不支持 | 支持 | 支持 |
通信协议 | HTTP和AMQP | HTTP | HTTP |
数据一致性 | Git保证数据一致性,Config-Server从Git读取数据 | 数据库模拟消息队列,Apollo定时读消息 | HTTP异步通知 |
Spring Cloud Config Server提供了可水平扩展的集中式配置服务。它使用可插拔的存储库层作为数据存储,该存储层目前支持本地存储,Git和Subversion。其核心功能:

Git Repository仓库(在实际生产环境中,一般需要自己搭建一个Git服务器。方便起见,建议使用了开源中国的gitee仓库或微软的github)ConfigSever,Spring Cloud微服务(如上图A、B、C)应用在启动的时候会从Config Server中来加载相应的配置信息 。前提是Spring Cloud微服务集成了Spring Cloud Config的客户端程序。Spring Cloud微服务尝试去从Config Server中加载配置信息的时候,Config Server会先通过git clone命令从远程Git Repository仓库克隆一份配置文件保存到本地 。这样当Git Repository远程仓库无法连接时,就直接使用Config Server本地存储的配置信息Git仓库中,所以配置文件天然的具备版本管理功能,Git中的Hook功能可以实时监控配置文件的修改虽然Spring Cloud config目前支持本地存储,Git和Subversion,但是基于配置版本审核、管理,以及可用性的考量基础,几乎最终都是选择git作为Spring Cloud config的配置仓库的管理工具。
考虑github连接太慢,下面使用gitee作为数据存储服务器
按照下面四个步骤我们来配置文件仓库:
git clone 远程库地址
git add 文件名 : 将工作区的文件添加到暂存区
git commit -m "日志信息" 文件名 : 将暂存区的文件提交到本地库
将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。
git push -u origin master 使用master分支,将本地仓库里面的内容提交到远程仓库


3.在克隆下来的仓库中新建配置文件,将之前系列中的rabc项目配置文件和sms配置文件复制进去,并进行改名,改名规则如下:
其文件命名规则为:{application}-{profile}.yml
application表示项目的名称,即:spring.application.name的配置值profile代表基础环境,通常是指:pro(生产)、dev(开发)、test(测试)等等。
4.添加文件到暂存区–>提交本地库


5.推送到远程仓库

将以上的本地配置文件及文件夹与远程仓库(gitee或github)同步,我们的git仓库构建工作就完成了。

和Eureka Server一样,netflix出品的Config Server也是基于SpringBoot项目的。
所以我们在spring-cloud新建一个module:dhy-server-config。

如果没有搭建父工程的,可以选择参考之前系列进行搭建,或者只引入相关依赖,测试使用
通过maven坐标引入关键类库:spring-cloud-config-server
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>StudyCloudWithMe</artifactId>
<groupId>dhy.xpy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dhy-server-config</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>在项目启动类上面加上@EnableConfigServer注解

在application.yml中进行config server的基本配置。
server:
port: 8771 #端口自定义
spring:
application:
name: dhy-server-config #config server项目名称
cloud:
config:
server:
git:
uri: https://gitee.com/DaHuYuXiXi/config-configuration-warehouse.git
#searchPaths: zimug-server-config-repo
username: xxx
password: xxx
#读取分支
label: master 如果把配置文件放在目录中提交上去,那么searchPaths就是该目录的名字,指明我们需要的配置文件在哪个目录下面
config server构建完成之后,我们可以通过浏览器URL访问测试,读取配置文件。
其配置文件的读取与URL之间的映射关系如下:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties{application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。{profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,对应到配置文件上来就是以
application-{profile}.yml加以区分,例如application-dev.yml、application-sit.yml、application-prod.yml。{label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,那就可以通过不同的 label
来控制访问不同的配置文件了。所以我们的这两个配置文件,可以通过如下的URL查看配置信息(以dhy-service-sms-dev.yml为例):


通过访问以上地址,如果可以正常返回数据,则说明配置中心Config Server服务端一切正常。至此,说明config server和git远程仓库之间的配置同步已经通了(红色边框部分)。

但是,大家可以明显的感觉到这里遗留了一个问题:
那就是任何一个人都可以通过浏览器URL去访问任何一个项目的配置文件。关于Spring Cloud config配置的安全与权限管理做的肯定是没有apollo那么好,但是也是有一些可以自己实现的安全认证方式,否则就太不安全了。我们后面的章节会讲到。

从上图中,通过前面章节的讲解,我们已经实现了如下内容
Git Repository仓库,我们已经可以向仓库提交配置文件了Config Server做统一的配置管理,可以从配置仓库拉取配置文件本节就为大家讲解第三步:微服务(config 客户端)从config server获取配置的方法。
仍然以dhy-service-sms和dhy-service-rbac服务(springboot)使用Spring Cloud Config集中配置管理为需求,为大家讲解。
在dhy-service-sms和dhy-service-rbac服务的pom.xml中引入spring-cloud-starter-config依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>src/main/resources/bootstrap.yml配置(以dhy-service-sms为例),来指定config server,例如:
spring:
application:
name: dhy-service-sms
cloud:
config:
uri: http://localhost:8771
label: master
profile: dev这里需要格外注意:上面这些属性必须配置在
bootstrap.yml或properties文件中,而不是application.yml中,config配置内容才能被正确加载。因为bootstrap.yml加载优先级高于application.yml,保证在应用一起动时就去加载配置,对于Spring中一些自动装载类来说这很重要。
当使用 Spring Cloud Config Server 的时候,你应该在 bootstrap.yml 里面指定 spring.application.name 和spring.cloud.config.server.git.uri和一些加密/解密的信息
加载过程:在Spring Cloud Config 项目 中configclient 服务启动后,默认会先访问bootstrap.yml,然后绑定configserver,然后获取application.yml 配置。如果仅仅在application.yml 配置了url:http://127.0.0.1:8080 这样默认会使用8888端口(配置无效)。 所以, 我们将绑定configserver的配置属性应该放在bootstrap.yml文件里。
以application.yml 为配置文件启动日志会有如下信息:
[z_cloud2_config_server_cluster_client] INFO org.springframework.cloud.config.client.ConfigServicePropertySourceLocator - Fetching config from server at: http://localhost:8888
[z_cloud2_config_server_cluster_client] WARN org.springframework.cloud.config.client.ConfigServicePropertySourceLocator - Could not locate PropertySource: I/O error on GET request for "http://localhost:8888/zCloud2ConfigServerClusterClient/default": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
[z_cloud2_config_server_cluster_client] INFO 当使用 Spring Cloud 的时候,配置信息一般是从 config server 加载的,为了取得配置信息(比如密码等),你需要一些提早的或引导配置。
因此,把 config server 信息放在 bootstrap.yml,用来加载真正需要的配置信息。
这是由spring boot的加载属性文件的优先级决定的,你想要在加载属性之前去spring cloud config server上取配置文件,那spring cloud config相关配置就是需要最先加载的,而bootstrap.properties的加载是先于application.properties的,所以config client要配置config的相关配置就只能写到bootstrap.properties里了。
简而言之: Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。
最后,我们把application.yml中的配置部注释掉(如下图)。因为这部分配置,我们已经全都放到git 仓库中进行集中管理,我们的服务通过config server就可以获取到。

dhy-service-sms项目在启动的时候去config server:http://localhost:8771加载配置。加载的是dhy-service-sms的master分支的dev环境的配置文件,也就是:https://gitee.com/DaHuYuXiXi/config-configuration-warehouse.git/dhy-service-sms-dev.yml,完全和我们的预期一致(和本文上面内容的配置一致)。

eureka相关的配置不是注释掉了么?
是本地应用注释掉了,但是我们已经把它转移到远程配置管理仓库中了。
这也再次验证了,aservice-sms正确的应用了远程git仓库和config server进行集中的配置管理。

在前面章节我们已经为大家介绍了Spring Cloud config进行配置管理的基本流程。在使用Config Server的时候,我们可以通过一些固定模式的http-URL,没有任何限制的访问到项目的配置文件信息,这样很不安全。
为了解决这个问题,我们可以使用spring security进行简单的basic安全认证(也可自定义认证方式,这里不做扩展,需要深入去学习Spring Security)
在dhy-server-config的pom文件中增加依赖:
<dependency>
<!-- spring security 安全认证 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>配置文件application.yml增加security配置:
spring:
security:
basic:
enabled: true #启用基本认证(默认)
user:
name: xxx #自定义登录用户名
password: xxx #自定义登录密码启动服务,测试一下。请求 http://localhost:8771/dhy-service-sms-dev.yml ,会发现弹出了security的basic登录验证:

输入我们自定义的用户名密码,返回请求配置信息。这种方式实际上也是一种简陋的安全认证方式,但总比没有强。
当config server增加了登录认证之后,我们的微服务客户端想要正确的获取配置信息,在发送请求的时候也要携带用户名密码。
修改配置文件bootstrap.yml,增加username和password配置:

首先启动config server服务端项目:dhy-server-config,然后启动dhy-service-sms和dhy-service-rbac服务。

至此,spring cloud config安全配置完成~
配置发生更改之后,将配置值的结果更新到客户端程序中。因此我们首先要明白两个问题:
在谈配置热更新以前,我们先将配置分成两类:
第一类配置的热更新也不是完全无法做到,可以自己写程序对配置数据变化进行监听,然后重新初始化其关联对象就可以实现。
2.spring cloud config可以对哪些注解标注的配置进行刷新。下面两个例子都可以将"user.init.password"键对应的值热更新到password和defaultPwd对象上。
3.这两个注解需要结合@RefreshScope注解使用才能使配置热更新生效。
@RefreshScope //这里需要加上RefreshScope注解
@ConfigurationProperties(prefix = "user.init")
public class User{
private String password;
}下文中会针对这种@Value注解的方法为例进行讲解。
因为config底层是基于RefreshEndpoint实现的配置刷新,因此需要引入actuator相关依赖,开放相关的端点
该依赖是加在客户端,是客户端需要刷新配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>actuator为我们提供“/actuator/refresh”配置刷新接口
例如: 我们这里需要动态刷新dhy-service-sms的配置信息,我们就将下面的配置加在gitee仓库中该微服务对应的yml文件里面
management:
endpoints:
web:
exposure:
include: refresh,health
在需要进行配置刷新的类上使用@RefreshScope,user.init.password对应的配置对象defaultPwd的值就初步具备刷新的能力。
@RestController
@RequestMapping("/sms")
@RefreshScope //不能忘记
public class SmsController {
@Value("${dhy.like}")
private Integer like;
@GetMapping(value = "/send")
public AjaxResponse send() {
System.out.println(like);
return AjaxResponse.success("短信发送成功,短信内容为: 大忽悠喜欢"+like);
}
}使用postman向“/sms/send”接口发送请求,测试配置是否能够正常拉取到

去gitee上面手动更改配置内容如下

当我们更改了gitee上面的配置文件后,配置中心config已经完成了最新版配置的拉取,还剩客户端需要从config配置中心手动拉取到最小的配置,下面验证config配置中心已经是最新的配置了

这里的乱码是因为没有提前告知浏览器返回的中文字符用什么字符集进行解析造成的,可能浏览器使用gbk解析,但是gitee上面是utf-8编码的
POST请求发送到http://localhost:2333/actuator/refresh,触发dhy-service-sms的配置进行刷新,返回值是刷新了哪些配置项
客户端验证是否得到最新结果:

那么有没有一种方法,能够实现配置修改之后,自动去向http://localhost:2333/actuator/refresh发送请求,更新配置?
是有方法的,但是不好。实际生产中几乎没法用,不好也给大家说说,学习一下。
我们可以在Git仓库中配置一个webhook,所谓webhook的作用就是每当git仓库有接收到push代码请求时,都会去向自定义指定URL发送POST请求。我们完全可以利用webhook进行配置的自动刷新。

这是一种方法给大家放在这学习一下,但是笔者重来没这么做过,基于以下几点原因:
基于以上原因,都不如自己决定向哪里发送/actuator/refresh请求。甚至写一个简单的管理程序,都比使用webhook强。当然如果我们想通过spring cloud config实现微服务配置的全量刷新、批量刷新、局部刷新,还有终极解决方案,那就是结合Spring Cloud Bus使用,后面章节我们会讲到。

Config Server实现高可用的原理比较简单:因为Config Server 是用Spring Boot构建的, 所以我们完全可以把它当做微服务注册到eureka,启动多个实例向eureka注册,并对外提供服务即可。
所以:
其实Config Server作为一个服务向eureka注册,和普通的微服务向eureka注册没有什么区别。
为了加深大家印象,我就再讲一遍:要想让Config Server实现服务注册功能,首先引入spring-cloud-starter-netflix-eureka-client。
<!-- spring cloud config 服务端包,这个包是一定存在的,否则就不是Config Server了-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- eureka client 端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>在application.yml加入eureka相关的配置
server:
port: 8771
spring:
application:
name: dhy-server-config
cloud:
config:
server:
git:
uri: xxxx
searchPaths: xxxx
username: xxxx
password: xxx
security:
basic:
enabled: true #启用基本认证(默认)
user:
name: xxxx
password: xxx
eureka:
client:
serviceUrl:
register-with-eureka: true
fetch-registry: true
defaultZone: http://dhy:centerpwd@peer1:8761/eureka/eureka/,http://dhy:centerpwd@peer2:8761/eureka/eureka/,http://dhy:centerpwd@peer3:8761/eureka/eureka/
instance:
preferIpAddress: true在dhy-server-config的启动类上加上@EnableEurekaClient注解

此时启动config server,再去看eureka注册中心,验证是否注册成功即可
我们的微服务客户端(如:dhy-service-sms和dhy-service-rbac)作为config client需要调整配置。

