分布式配置中心:spring cloud config
前言
Spring Cloud Config是Spring Cloud团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持的,它分为服务端与客户端两个部分。其中服务端也成为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密/解密信息等访问接口;而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。Spring Cloud Config实现了对服务端和客户端中环境变量和属性配置的抽象映射,所以它除了适用于Spring构建的应用程序之外,也可以在任何其他语言运行的应用程序中使用。由于Spring Cloud Config实现的配置中心默认采用Git来存储配置信息,所以使用spring Cloud Config构建的配置服务器,天然就支持对微服务应用配置信息的版本管理,并且可以通过Git等客户端工具来方便地管理和访问配置内容。当然它也提供了对其他存储方式的支持,比如SVN仓库、本地化文件系统。接下来,我们从一个简单的入门示例开始学习Spring Cloud Config服务端以及客户端的详细构建与使用方法。
快速入门
在本节中,我们将演示如何构建基于Git存储的分布式配置中心,同时对配置的详细规则进行讲解,并在客户端中演示如何通过配置指定微服务应用的所属配置中心,并让其能够从配置中心获取配置信息并绑定到代码中的整个过程。
构建配置中心
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
设置主类
创建Spring Boot的程序主类,并添加@EnableConfigServer注解,开启Spring Cloud Config的服务端功能。
package com.configserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
配置属性文件
在application.properties中添加配置服务的基本信息以及Git仓库的相关信息,如下所示:
spring.application.name=config-server
server.port=7001
# git管理配置
spring.cloud.config.server.git.uri=https://github.com/ZhongZeWei/springCloudConfig.git
spring.cloud.config.server.git.searchPaths=config-repo/
spring.cloud.config.server.git.username=你的GITHUB账户
spring.cloud.config.server.git.password=你的GITHUB密码
其中Git等配置信息分别表示如下内容:
▪️spring.cloud.config.server.git.uri:配置Git仓库位置
▪️spring.cloud.config.server.git.searchPaths:配置仓库路径下单相对搜索位置,可以配置多个
▪️spring.cloud.config.server.git.username:访问Git仓库的用户名
▪️spring.cloud.config.server.git.password:访问Git仓库的用户密码
到这里,使用一个通过Spring Cloud Config实现,并使用Git管理内容等分布式配置中心就完成了。我们可以将该应用先启动起来,确保没有错误产生,然后进入下面等地学习内容。
配置规则详解
为了验证上面完全的分布式配置中心config-server,根据git配置信息中指定的仓库位置,在https://github.com/ZhongZeWei/springCloudConfig.git下创建config-repo目录作为配置仓库,并根据不同环境新建下面4个配置文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pzBWccOF-1600188537709)(media/1546683655739.png)]
github中的文件
在这4个配置文件中均设置了一个from属性,并为每个配置文件分别设置了不同的值,如下所示:
▪️from=git-default-1.0
▪️from=git-dev-1.0
▪️from=git-test-1.0
▪️from=git-prod-1.0
完成了这些准备工作之后,我们就可以通过浏览器、POSTMAN或CURL等工具直接来访问我们的配置内容了。访问配置信息的URL与配置文件的映射关系如下所示:
- / { 应用名 } / { 环境名 } [ / { 分支名 } ]
- / { 应用名 } - { 环境名 }.yml
- / { 应用名 } - { 环境名 }.properties
- / { 分支名 } / { 应用名 } - { 环境名 }.yml
- / { 分支名 } / { 应用名 } - { 环境名 }.properties
上面的url会映射{application}-{profile}.properties对应的配置文件,其中{label}对应Git上不同的分支,默认为master。我们可以尝试构造不同的url来访问不同的配置内容,比如要访问kyle分支,didispace应用的prod不同的配置内容,比如,要访问master分支,config应用的prod环境,就可以访问这个http://localhost:7001/config/prod/master,并获得如下返回信息。
访问测试http://localhost:7001/config/prod/master
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohJMsR5g-1600188537710)(media/1546683781717.png)]
config-server通过Git在本地仓库暂存,可以有效防止当Git仓库出现故障而引起无法加载配置信息的情况。我们可以通过断开网络,再次发起http://localhost:7001/config/prod/master请求,在控制台中可输出Could not pull remote for kyle,但是它依然会为该请求返回配置内容,这些内容源于之前访问时存于config-server本地文件系统中的配置内容。
客户端配置映射
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
设置启动类
package com.configclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
设置属性文件
#spring.application.name不能乱写,要和属性文件的应用名一致
spring.application.name=config
spring.cloud.config.profile=prod
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7001/
server.port=7002
这里需要格外注意,上面的这些属性必须配置在bootstrap.properties中,这样config-server中的配置信息才会被正确加载。
创建controller
package com.configclient.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RefreshScope
@RestController
public class TestController {
@Value("${from}")
private String from;
@RequestMapping("/from")
public String from() {
return this.from;
}
public void setFrom(String from) {
this.from = from;
}
public String getFrom() {
return from;
}
}
测试
访问http://localhost:7002/from
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6HUWPe3d-1600188537714)(media/1546689444497.png)]
服务端详解
基础架构
其中主要包含下面几个要素。
- 远程Git仓库:用来存储配置文件,上一节我存储了应用名为config的多环境配置文件:config-{profile}.properties。
- Config Server:这是一个分布式配置中心,即config-service模块,在该工程中指定了Git仓库的位置。
- 本地Git仓库:在Config Server的文件系统中,每次客户端请求获取配置信息时,Config Server从Git仓库获取最新配置到本地,然后在本地Git中读取并返回。当远程仓库无法获取时,直接将本地内容返回。
- ServiceA、ServiceB:具体的微服务应用它们指定了Config Server的地址,从而实现从外部化获取应用本身要用到的配置信息。这些应用在启动的时候,会向Config Server请求获取配置信息来进行加载。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J9dIAg8P-1600188537715)(media/1546689992179.png)]
客户端应用从配置管理中获取配置信息遵从下面的执行流程:
1.应用启动时,根据bootstrap.yml(或者bootstrap.properties)中配置的应用名{application}、环境名{profile}、分支名{label},向Config Server请求获取配置信息。
2.Config Server根据自己维护的Git仓库信息和客户端传递过来的配置定位信息去查找配置信息。
3.通过 git clone命令将找到的配置信息下载到Config Server的文件系统中。
4.Config Server创建Spring的ApplicationContext实例,并从Git本地仓库中加载配置文件,最后将这些配置内容读取出来返回给客户端应用。
5.客户端应用在获得外部配置文件后加载到客户端的ApplicationContext实例,该配置内容的优先级高于客户端Jar包内部的配置内容,所以Jar包中重复的内容将不再被加载。
Config Server巧妙的通过git clone将配置信息存于本地,起到了缓存的作用,即使当Git服务端无法访问的时候,依然可以去Config Server中的缓存内容进行使用。
Git配置仓库
由于Spring Cloud Config默认使用Git,所以对Git的配置非常简单,只需要在Config Server的application.properties中设置spring.cloud.config.server.git.uri属性,为其指定Git仓库的网络地址和账户信息即可
# git管理配置
spring.cloud.config.server.git.uri=https://github.com/ZhongZeWei/springCloudConfig.git
spring.cloud.config.server.git.searchPaths=config-repo/
spring.cloud.config.server.git.username=2205101603@qq.com
spring.cloud.config.server.git.password=940895133133Aa
另外,Spring Cloud Config可以使用本地物理环境来代替Git远程仓库。
spring.cloud.config.server.native.search-locations=classpath:config-repo/
spring.profiles.active=native
使用本地物理环境来代替Git远程仓库的方式也仅用于开发与测试,在生产环境中务必搭建自己的git仓库来存储配置资源。
占位符配置URI
{application}
、{profile}
、{label}
这些占位符除了标识配置文件规则外,还可以对Git的uri配置,
如:
spring.cloud.config.server.git.uri=https://github.com/hellxz/SpringCloudlearn/config-repo/{application}
此时spring.application.name
的值会填充到这个uri中,从而实现一个应用对应一个Git仓库目录的配置效果
配置多个仓库
config server
除了可以通过application
和profile
模式来匹配配置仓库之外,还支持通过带有通配符的表达式来匹配,以实现更为复杂的配置要求。并且当我们有多个匹配规则的时候,还可以通过逗号来分割多个{application}/{profile}配置规则,比如:
spring.cloud.config.server.git.uri=http://git.oschina.net/zhihaomiao/config-repo
spring.cloud.config.server.git.repos.dev.pattern=dev/*
spring.cloud.config.server.git.repos.dev.uri=file://home/git/config-repo
spring.cloud.config.server.git.repos.test=http://git.oschina.net/test/config-repo
spring.cloud.config.server.git.repos.prod.pattern=prod/pp*,online/oo*
spring.cloud.config.server.git.repos.prod.uri=http://git.oschina.net/prod/config-repo
上述配置内容通过配置内容spring.cloud.config.server.git.uri
属性,指定了一个默认的仓库位置,当使用{application}/{profile}
模式未能匹配到合适的仓库时,就将在该默认仓库位置下获取配置信息。除此之外,还配置了三个仓库,分别是dev
,test
,prod
。其中,dev
仓库匹配dev/*
的模式,所以无论profile
是什么,它都能匹配application
名称为dev
的应用。并且我们可以注意到,它存储的配置文件位置还采用了config server
的本地文件系统中的内容。对于此位置,我们可以通过访问http://localhost:9090/dev/profile
的请求来验证到该仓库的配置内容,其中profile
可以是任意值。而test
和prod
仓库均使用git仓库的存储,并且test仓库未配置匹配规则,所以它只匹配application
名为test
的应用;prod
仓库则需要匹配application
为prod
并且profile
为pp
开头,或者application
为online
并且profile
为oo
开头的应用和环境。
当配置多个仓库的时候,config server
在启动的时会直接克隆第一个仓库的配置库,其他的配置只有在请求时才会克隆到本地,所以对于仓库的排列可以根据配置内容的重要程度有所区分。另外,如果表达式是以通配符开始的,那么需要使用引号将配置内容引起来。
注意
- 这种方式的配置肯定是不好的,造成管理的混乱,
spring.cloud.config.server.git.uri
配置是默认的回退配置,如果其他条件都不符合或者是配置的属性在其匹配的模式中并没有得到,那么就会去spring.cloud.config.server.git.uri
去匹配。 - 本列中的repos后面的dev,test,prod都是对应着application应用名称。
- 上面的pattern配置如果在yml中配置的话,那么还有另外一种写法,可以看官网,使用-这边就不展示了。
子目录存储
通过spring.cloud.config.server.git.searchPaths
来定位到Git仓库的子目录中,相当于在uri后加上searchPaths的目录。
searchPaths参数的配置也支持使用{应用名}、{环境名}、{分支名}占位符
比如spring.cloud.config.server.git.searchPaths={应用名}
,通过这样的配置,我们能让每一个应用匹配到自己的目录中。
访问权限
config server
在访问git仓库的时候,若采用http的方式进行认证,那么我们需要增加username
和password
属性来配置账户,比如快速入门的demo
spring.cloud.config.server.git.username=自己Git账户
spring.cloud.config.server.git.password=自己的git账户密码
若不采用http的认证方式,我们也可以采用ssh的方式,通过生成key并在git仓库中进行配置匹配以实现访问。
svn配置仓库
config server
除了支持git
仓库之外,也能使用svn
仓库,只需要如下配置。
-
在pom.xml中引入
svn
的依赖配置,让config server
拥有读取svn
内容的能力:<!--SVN--> <dependency> <groupId>org.tmatesoft.svnkit</groupId> <artifactId>svnkit</artifactId> </dependency>
-
在
application.properties
中使用svn
的配置属性来指定svn
服务器的位置,以及访问的账户名与密码:
spring.cloud.config.server.svn.uri=svn://localhost:443/didispace/config-repo
spring.cloud.config.server.svn.username=username
spring.cloud.config.server.svn.password=password
通过上面的配置修改,config server
就可以使用svn
作为仓库来存储配置文件了,对于客户端来说,这个过程是透明的,所以不需要做任何变动。
本地仓库
在使用了git
或svn
仓库之后,文件都会在config server
的本地文件系统中存储一份,这些文件默认会被存储以config-repo
为前缀的临时目录中,比如/tmp/config-repo-<随机数>
的目录。由于其随机性以及临时目录的特性,可能会有一些不可预知的后果,为了避免将来可能会出现的问题,最好的方法就是指定一个固定的位置来存储这些重要信息。我们只需要通过spring.cloud.config.server.git.basedir
或spring.cloud.config.server.svn.basedir
来配置一个我们准备好的目录即可。
本地文件系统
spring cloud config
也提供了一种不适用git
仓库或svn
仓库的存储方式,而是使用本地文件系统的存储方式来保存配置信息。实现方式也简单,只需要配置属性spring.profile.active=native
,config server
会默认从应用的src/main/resource
目录下搜索配置文件。如果需要指定搜索配置文件的路径,我们可以通过spring.cloud.config.server.native.searchLocations
属性来指定具体的配置文件位置。
虽然spring cloud config提供了这样的功能,但是为了支持更好的内容管理和版本控制等强大功能,还是推荐使用git仓库的方式
健康监测
配置好了Git或SVN之后,为了能确保能连通仓库,我们需要为其实现健康监测功能,来判断Git或SVN仓库是否可以访问
我们配置了spring.cloud.config.server.git.uri
,而且依赖了spring-boot-actuator
的依赖,会自动mapped到/health的端点,我们可以访问测试一下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdM83ToJ-1600188537715)(media/1546762863641.png)]
如果我们无法连接到配置仓库的uri那么status
就会变成DOWN
在org.springframework.cloud.config.server.config
包中有一个ConfigServerHealthIndicator
的健康检测器,这个检测器会不断地检查配置的uri是否可以连通。
如果不想使用这个健康检测器,也可以通过使用spring.cloud.config.server.health.enabled=false
来禁用它。
属性覆盖
Config Server提供一个叫Property Overrides
即属性覆盖
的功能,通过spring.cloud.config.server.overrides
属性来设置键值对的参数,这些参数会以Map形式加载到所有Config Client的配置中。
官方举例:
spring.cloud.config.server.overrides.name=didi
spring.cloud.config.server.overrides.from=shanghai
通过属性覆盖配置的参数不会被Config Client修改,并且Config Client获取配置信息时就会得到这些信息,使用这种特性可以方便我们为客户端应用配置一些共同属性或默认属性,会优先覆盖git仓库的属性内容。这些属性不是强制的,可以通过改变客户端中的更高优先我级的配置方式来选择是否使用这些属性覆盖的默认值。
安全保护
配置中心存储的内容很敏感,所以必需做一些安保措施,使用Spring Security更方便一些。
只需要引入依赖并配置用户名和密码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
默认情况下会得到一名为user的用户,并在配置中心启动的时候在log中打印出来随机密码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9DENrMb-1600188537716)(media/1546763047714.png)]
当然大多数情况下我们不会使用这个随机密码,我们可以在config-server配置文件中指定用户和密码
#安全保护密码
security.user.name=user
security.user.password=123
通过上边的配置,配置中心已经开启了安全保护,这时候连接配置中心的客户端没有密码的情况下会返回401错误
只需要在config-client客户端中加入账号密码来通过安全校验,举例
#安全保护密码
spring.cloud.config.username=user
spring.cloud.config.password=123
加密解密
微服务开发中往往都是所属组织成员自行维护,在配置文件中会有很多敏感信息,比如数据库密码等,这些信息如果以明文存储和传输是很危险的,为解决这个问题,Config提供了对属性加密解密的功能,只需要在敏感信息的值加密后加上前缀{cipher}
,这主要是为了防止密文被用作密码并意外泄露。ps: 防的就是开发人员。
使用前提
重要:要使用加密和解密功能,您需要在JVM中安装全功能JCE(默认情况下不包括它)。您可以从Oracle下载“Java Cryptography Extension(JCE)Unlimited Strength Jurisdiction Policy Files”并按照安装说明进行操作(实质上,您需要将JRE lib / security目录中的两个策略文件替换为您下载的那些)。
JCE for JDK8下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
注意:上边说的是官方的说法,但是我发现一个很气的事实是我下载好这JCE的文件后,发现jdk1.8中已经有了这些文件,如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rEMKm1m-1600188537716)(media/1149398-20180713180122267-1063148348.png)]
可能是1.8自带了这些吧,按Spring Cloud 文档中的说法,把unlimited下的两个jar包复制到
{JAVA_HOME}/jre/lib/security
目录下,这里当然也没有自带的JCE这两个包去被覆盖,limited和unlimited区别在于limited对加密解密的长度有限制。
用在git仓库属性文件中增加以下内容:
spring.datasource.name=didi
spring.datasource.password={cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
.yml配置文件需要使用单引号括起来要解密的内容,.properties文件中的加密值不得用引号括起来。否则,该值不会被解密。
相关端点
完成了JCE的安装后,可以尝试启动配置中心,Spring Cloud 暴露出了几个端点
- /encrypt/status:查看加密功能状态的端点
- /key:查看密钥的端点
- /encrypt:对请求的body内容进行加密的端点
- /decrypt:对请求的body内容进行解密的端点
在没有设置密钥的时候,访问/encrypt/status端点会出现以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJFnnkoe-1600188537717)(media/1546764360439.png)]
配置密钥
在bootstrap.yml中设置密钥(对称性密钥)
#配置密钥
encrypt.key=testKey
访问/encrypt/status端点会出现以下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5OPHgDxn-1600188537717)(media/1546764609289.png)]
去掉config-server安全模块,使用post方式访问/encrypt端点测试加密效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHxYwD0h-1600188537718)(media/1546765241475.png)]
/decrypt解密测试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xkSfTMNe-1600188537718)(media/1546765314473.png)]
非对称加密
非对称加密相对对称加密的密钥生成与配置更加复杂,耗时也更多,但是更安全。
这里我们使用jdk自带的keytool工具来生成证书,它的位置在%JAVA_HOME%\bin\keytool.exe
生成密钥的命令如下,请酌情修改:
PS C:\Program Files\java\jdk1.8.0_161\bin> keytool -genkeypair -alias config-server -keyalg RSA -keystore D:\\config-server.keystore
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
[Unknown]: Hellxz Zhang
您的组织单位名称是什么?
[Unknown]: SN
您的组织名称是什么?
[Unknown]: SN
您所在的城市或区域名称是什么?
[Unknown]: JiNan
您所在的省/市/自治区名称是什么?
[Unknown]: ShanDong
该单位的双字母国家/地区代码是什么?
[Unknown]: ZH
CN=Hellxz Zhang, OU=SN, O=SN, L=JiNan, ST=ShanDong, C=ZH是否正确?
[否]: y
输入 <config-server> 的密钥口令
(如果和密钥库口令相同, 按回车):
Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore D:\\config-server.keystore -destkeystore D:\\conf
ig-server.keystore -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
另外,如果我们不想逐步的输入那些提示信息,可以使用-dname
来直接指定,而密钥库口令与密钥口令可使用-storepass
和-keypass
来直接指定。所以,我们可以通过下面的命令直接创建出与上述命令一样的密钥库:
$ keytool -genkeypair -alias config-server -keyalg RSA \
-dname "CN=zhaiyongchao, OU=company, O=organization, L=city, ST=province, C=china" \
-keypass 222222 \
-keystore config-server.keystore \
-storepass 111111 \
默认情况下,上述命令创建的密钥只有90天有效期。如果我们想要调整它的有效期,可以通过增加-validity
参数来实现,比如我们可以通过下面的命令,让密钥的有效期延长到一年:
$ keytool -genkeypair -alias config-server -keyalg RSA \
-dname "CN=zhaiyongchao, OU=company, O=organization, L=city, ST=province, C=china" \
-keypass 222222 \
-keystore config-server.keystore \
-storepass 111111 \
-validity 365 \
这里使用的是PowerShell,在win下测试的,这样操作后,在D盘生成了config-server.keystore的文件,我们将这个文件放到Config Server 的resources下(classpath)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AZj24TnW-1600188537718)(media/1546766607647.png)]
然后在application.properties加入相关配置信息
encrypt.key-store.location=config-server.keystore
encrypt.key-store.alias=config-server
encrypt.key-store.password=123456
encrypt.key-store.secret=123456
生成的时候密码都是盲输的,我都输的123456,如有修改,请用自己输入的
加密测试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4hwVluYI-1600188537719)(media/1546766698336.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q4gkXh0E-1600188537719)(media/1546766720941.png)]
每次加密的得到的结果都不一样
解密测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M7lA4vws-1600188537720)(media/1546766792326.png)]
高可用配置
为了实现配置中心的高可用,主要有两种方式
-
传统模式:不需做任何额外配置,起多个配置中心服务,所有配置中心都指向同一个Git仓库,客户端通过负载均衡进行调用配置中心服务,从而实现服务的高可用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7r9ucGO-1600188537720)(media/5225109-20bc7ff33cfd70c3.png)]
-
服务模式:将配置中心注册为微服务到注册中心,通过Eureka的服务治理进行负载均衡,而且也实现了自维护,这块在后边的客户端详解中讲。
客户端详解
URI指定配置中心
Spring Clout Config的客户端在启动的时候,默认会从工程的classpath下加载配置信息并启动应用,只有配置了spring.cloud.config.uri
的时候,客户端才会尝试连接Config Server拉取配置信息并初始化Spring环境配置。**我们必须将uri这个属性参数配置到bootstrap.yml(或.properties)中,这个配置文件的优先级大于application.yml和其它文件,这样才能保证能正确加载远程配置。**启动的时候客户端会去连接uri属性的值。
举例:
#spring.application.name不能乱写,要和属性文件的应用名一致
spring.application.name=config
spring.cloud.config.profile=prod
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7001/
例子中使用application.name和profile定位配置信息
服务化配置中心
服务端与客户端配置
服务端详解那一块说过可以把配置中心注册到注册中心,通过服务发现来访问Config Server拉取Git仓库中的配置信息。
下面的内容需要改造Config-Server和Config-Client
为ConfigServer和ConfigClient的pom.xml中分别加入Eureka的依赖
<!-- Spring Cloud Eureka的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
为ConfigServer和ConfigClient的属性文件中分别加入Eureka注册服务注册中心配置
#注册服务注册中心
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
依赖有了,注册中心的地址也有了,最后别忘了将这两个模块的主类加上@EnableDiscoveryClient
。
完成这一步我们可以先启动注册中心、ConfigServer、ConfigClient 看一看是否注册成功,如图,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KCKFtr0-1600188537721)(media/1546771210498.png)]
ConfigServer至此配置完毕。接下来我们为客户端使用服务发现的方式去调用ConfigServer
只需要在ConfigClient的bootstrap.properties中注掉spring.cloud.config.uri那一项,并添加以下内容:
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=CONFIG-SERVER
访问http://localhost:7002/from
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUiqXvwV-1600188537721)(media/1546771592127.png)]
测试成功,服务化配置中心配置完成。
失败快速响应与重试
spring cloud config
的客户端会预先加载很多其他信息,然后再开始连接config server
进行属性的注入,当我们构建的应用比较复杂的时候,可能在连接config server
之前花费太多的启动时间,而在一些特殊的场景下,我们希望可以快速知道当前应用是否能顺利地从config server
获取到配置信息,这对在初期构建调试环境时,可以减少很多等待启动的时间。要实现客户端优先判断config server
获取是否正常,并快速响应失败内容,只需在bootstrap.properties
中配置参数spring.cloud.config.failFast=true
即可。
测试一下,在没有该参数前,不启动config server
,直接启动客户端应用,可以获得下面的报错信息,同时,在报错之前,可以看到客户端应用已经加载了很多内容,比如controller的请求,只有在属性注入的时候报错了,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHLYYd52-1600188537722)(media/1546771914372.png)]
加上spring.cloud.config.failFast=true
参数之后,再启动客户端应用,可以获得下面的报错信息,并且前置的加载内容少了很多,这样通过该参数有效避免了当config server
配置有误时,不需要多等待前置的一些加载时间,实现快速失败信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJHA3OIm-1600188537722)(media/1546772036768.png)]
上面,我们演示了当config server宕机或是客户端配置不正确导致连接不到而启动失败的情况,快速响应的配置可以发挥比较好的效果。但是,若只是因为网络波动等其他间歇性原因导致的问题,直接启动失败似乎代价有点高。所以,config客户端还提供了重试的功能,在开启重试功能前,先确保已经配置了spring.cloud.config.failFast=true
,在进行下面的操作:
- 在
pom.xml中增加spring.retry
和spring-boot-starter-aop
依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
**不需要做其他任何配置,启动客户端应用,在控制台中可以看到如下内容,客户端连接config server
失败之后,继续尝试,直到第6次失败后,才返回错误信息。**通过这样的重试机制,可以避免一些间歇性问题引起的失败导致客户端应用无法启动的情况。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8mVXprft-1600188537723)(media/1546772159720.png)]
如果是配置具体的uri的话那么就会重试6次的uri配置,如果是配置的eureka
注册的话,就会发6次http://localhost:8888
失败。
若对默认的最大重试次数和重试间隔等设置不满意,还可以通过下面的参数进行调整:
spring.cloud.config.retry.multiplier
:初始重试间隔时间(单位为毫秒),默认是1000毫秒。spring.cloud.config.retry.initial-interval
:下一个间隔的乘数,默认是1.1,所以当最初间隔式1000毫秒时,下一次失败后的间隔为1100毫秒。spring.cloud.config.retry.max-interval
:最大间隔时间,默认是2000毫秒。spring.cloud.config.retry.max-attempts
:最大重试次数,默认为6次。
获取远程配置
在快速入门中,我们对 {application}
, {profile}
, {label}
这些参数已经有了一定的了解。在git仓库中,一个形如 {application}-{profile}.properties
或是 {application}-{profile}.yml
的配置文件,通过uri请求和客户端配置访问对应可以总结如下:
- 通过向config-server发送get请求以直接的方式获取,可用下面的链接形式:
不带{label}分支信息,默认访问master分支,可使用
- /{application}-{profile}.yml
- /{application}-{profile}.properties
访问http://localhost:7001/config-prod.properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tT0Mvb4v-1600188537723)(media/1546772634379.png)]
带{label}分支信息,可使用:
- /{label}/{application}-{profile}.yml
- /{application}/{profile}[/{label}]
- /{label}/{application}-{profile}.properties
访问http://localhost:7001/master/config-prod.properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqSu4hKU-1600188537724)(media/1546772687955.png)]
- 通过客户端配置方式加载的内容如下所示
- spring.application.name: 对应配置文件中的{application}内容
- spring.application.profile: 对应配置文件中{profile}内容
- spring.application.config.label: 对应分支内容,如不配置,默认为master
动态刷新配置
有时候需要对配置内容做些实时更新,那么spring cloud config
是否可以实现呢?肯定是可以的,下面对快速入门进行一些改造.
回顾一下快速入门,
config-repo:在git上新建的一项目模块,只要是做配置中心的,其中存储了应用名为
zhihao的多环境配置文件,配置文件中有二个配置参数
from和
spring.datasource.nameconfig-server
:配置了git仓库的服务端config-client
: 指定了config-server
为配置中心的客户端,用来访问配置服务器以获取配置信息。该应用中提供了一个/from接口,访问config-prod.properties中的from
属性,
当前配置文件:
from=git-test-1.0
spring.datasource.name=test
spring.datasource.password={cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
在线修改之后(push到git远程仓库),
from=git-test-2.0
spring.datasource.name=test
spring.datasource.password={cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
测试,http://localhost:7002/from
发现访问客户端的接口,页面显示还是修改前的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPzmMdXl-1600188537724)(media/1546773143109.png)]
接下来,我们将config-client端做一些改造以实现配置信息的动态刷新。
- 在
config-client
的pom文件中新增spring-boot-starter-actuator
监控依赖,其中包含/refresh
端点的实现,该端点将用于实现客户端应用配置信息的重新获取与刷新。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 在客户端上需要注入配置值的类上加入注解
@RefreshScope
,该注解支持自定义属性的刷新。
package com.configclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@RefreshScope
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
-
重新访问
config-client
,访问一次可以看到当前的配置[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4anJKftP-1600188537725)(media/1546773332861.png)]
-
修改git上的配置
from=git-test-3.0 spring.datasource.name=test spring.datasource.password={cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
-
在次访问http://localhost:7002/from,看到配置信息没有改变
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bnBwFpw-1600188537725)(media/1546773364214.png)]
-
通过post请求发送到
localhost:7001/refresh
,可以看到返回的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7cxorAW-1600188537726)(media/1546773455866.png)]
- 再次访问http://localhost:7002/from,看到更新后的值了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lgZwkRvP-1600188537726)(media/1546773491285.png)]
通过上面的介绍,该功能还可以同git仓库的web hook功能进行关联,当有git提交变化时,就给对应的配置主机发送/refresh
请求来实现配置信息的实时更新。但是,当我们的系统发展壮大之后,维护这样的刷新清单也将成为一个非常大的负担,而且很容易犯错,可以使用spring cloud bus
来解决这个问题。