文章内容来自 《springcloud微服务架构开发实战》 董超 胡炽维
微服务配置处理好的问题:
1.配置数据和服务实例不放在同一个地方,部署也不应该包含这些配置数据,而是当微服务启动时从一个集中的配置源中进行读取。
2.解决非侵入式配置数据的织入。在每个微服务中不希望通过硬编码的方式从某个配置文件、原程仓库或者数据中读取配置数据,而是最好通过一种更通用的方式让微服务可以快速加载这些配置资源。
3.对配置文件的集中式管理,可以非常方便地对微服务的配置进行统一修改和发布,并能够建立版本机制,以便后续进行配置数据的回溯。
4.保障配置服务的高可用性;
Spring Cloud Config简介
优势:
·提供配置服务器(Config Server)和配置客户端(Config Client)两种角色,便于部署和使用,使开发者可以集中式管理分布式环境下的应用配置。
·配置服务器集中对配置资源进行管理,并支持多种配置资源存储方式,如Git、SVN及文件系统。
·通过对Git、SVN库的支持,便于对配置文件进行版本管理,后续可以对配置文件的变更做审查。
·基于Spring环境,与Spring Boot深度整合,在应用中通过几个简单的注解就可以实现配置的统一管理,而不需要过多的投入。
·Spring Cloud Config提供与Spring Boot配置类似的机制,可以非常容易地实现对应用开发环境、测试环境、仿真环境和生产环境的配置、切换和迁移等处理。
·配置服务器可以方便地与Eureka和Consul等进行整合,快速构建一个高可用的配置服务。·配置服务器也可用于其他语言开发的服务中。
Server作为配置中心的服务端承担如下作用:
·当配置客户端获取配置时,服务端及时从Git仓库中获取配置副本,从而保证配置数据为最新。
·支持从yml、json、properties等文件加载配置;
·配合Eureke可实现服务发现,配合Cloud Bus(后面会详细介绍)可实现配置推送更新。
·默认配置存储基于Git仓库(可以切换为SVN),从而支持配置的版本管理。
对于Client,即各个微服务,使用则非常方便,只需要在引导配置文件(bootstrap.properties或bootstrap.yml)中声明所使用的配置服务器地址即可。
实例:本节将在前几章的工程基础上进行快速改造,将引入一个新的配置服务器,并将ProductService和UserService微服务中的配置统一存放到Git仓库中,最后对这两个微服务进行改造,升级为配置客户端,从而实现配置的统一管理。
7.2.1 构建配置服务器
配置服务器是一个标准的Spring Boot应用,其可以嵌入在其他服务中以提供配置服务,可以不作为一个独立的服务进行部署。以下内容还是将配置服务器独立成一个单独的服务器,并进行部署。
1.pom文件spring-cloud-config-server的依赖;
2.启动类加入@EnableConfigServer
3.编写配置文件(application.properties)
server.port=8888
spring.application.name=configserver
spring.cloud.config.server.git.uri=https://github.com/cd826/SpringcloudSamplesConfig
spring.cloud.config.server.git.username=cd826dong@gmail.com
spring.cloud.config.server.git.password=hongyu206
spring.cloud.config.server.git.basedir=tmp/
#encrypt.key=cd826dong_ekey
spring.cloud.config.server.encrypt.enabled=false
eureka.client.service-url.defaultZone=http://localhost:8260/eureka
7.2.2 创建应用配置文件
在git 仓库配置application.properties,productservice.properties,productserver-dev.properties等;
使用http://localhost:8888/productservice/default,http://localhost:8888/productservice/dev访问,
7.2.3 升级微服务配置
使它们可以读取到之前存储在Git仓库中的配置,在项目中引入spring-cloud-starter-config的依赖;
然后,修改配置文件。对于Spring Boot应用来说有两个配置文件,一个是前面所使用的application.properties配置文件,另一个是bootstrap.properties。Spring Boot应用在启动时会根据bootstrap.properties配置文件创建一个引导上下文(Bootstrap Context)的文件,引导上下文将负责从外部加载配置属性并进行相应的解析,并作为Spring应用上下文(Application Context)的父上下文。此外,这两个上下文将共享一个从外部获取的Environment。默认情况,引导上下文的配置属性具有高优先级,不会被本地配置覆盖。由于引导上下文和应用上下文具有不同的含义,所以这里所要增加的配置属性信息就不是在application.properties配置文件中进行配置,而是在bootstrap.properties配置文件中进行配置。
7.2.4 启动测试
打包编译后,我们按照顺序启动EurekaServer、ConfigServer、UserService和ProductService,
当我们启动ProductService微服务时,Spring CloudConfig相关代码将根据bootstrap.properties所配置的服务名称和所要启动的profile向配置服务器的端点请求配置数据。配置服务器在收到配置数据请求后,根据相应的参数,从配置资源库(文件系统、Git或者SVN仓库,示例中是Git仓库)加载相应的配置文件,然后根据这些配置文件来构建数据,并将这些数据回传给配置客户端,最后Spring Boot应用框架将根据所获得的配置数据来构建应用上下文,并启动相应的应用。
7.2.5 @Value注解
Spring框架的应用是支持使用@value注解的方式以获取properties文件中的配置值;
比如使用@Value("${foo}")读取配置文件中foo属性值赋值给变量;
另外,虽然@Value支持注解在任一个字段上(当然相应的类必须能够被Spring的自动配置扫描到),但是从开发和管理角度上还是最好能够按照配置数据类别,分别构建承载集中配置的类,这样不至于由于配置数据太分散而造成难以管理的局面。比如,在我们开发的产品中常常将微信配置存放在一个配置类WechatProperties中进行管理,然后通过**@ConfigurationProperties注解,实现统一对配置属性自动注入**
7.2.6 关于配置服务的默认配置
spring-cloud-config-server.jar包中有一个默认配置文件configserver.yml,也就是说当设置spring.application.name=configserver时,则会默认加载该配置文件;
从配置中可以看到,配置服务器默认使用8888端口,默认使用Git作为配置文件存储仓库,并从https://github.com/spring-cloud-samples/config-repo这个Git仓库中查找配置文件。
7.2.7 Spring配置加载顺序
多种方式进行配置:配置文件(properties或yml)、命令行参数,此外还有系统环境变量、JVM的参数等。
·命令行参数:命令行参数使用–xxx=xxx格式在启动时传递,比如:–server.port=2300,就是将服务的端口设置为2300。这里的参考可以是Spring Boot框架的参数,也可以是我们自定义的参数或属性配置。
·从java:comp/env加载的JNDI属性。
·Java系统属性:通过-Dxxx=xxx格式设置,只是优先级比上面的配置低。
·操作系统环境变量:这里需要注意的一点是,有些操作系统配置时不支持使用点“.”进行分割,则需要将点替换成下画线。例如,server.port需要替换成server_port。
·RandomValuePropertySource:使用random.*属性进行配置,主要是在系统配置中需要使用随机数的地方使用,如foo.securityid=${random.value}。
·特定应用的properties或yml配置文件:这些文件名称的命名格式为application-{profile}.properties或者yml,通过指定所要使用的profile来加载,例如前面使用的application-dev.properties配置文件。
·应用配置文件application.properties或yml文件:为Spring Boot应用所默认加载的配置文件,可以通过上面的配置进行全部或部分配置属性的覆写。
·@Configuration、@PropertySource或@ConfigurationProperties所指向的配置文件,其中@ConfigurationProperties可以批量按照一定规范将配置注入到一个Bean中,但这些配置的优先级较低。
7.3 配置资源库
7.3.1 配置资源规则详解
核心:Spring Cloud Config通过EnvironmentRepository来获得Environment对象解决;
该对象是对Spring的Environment(包括做为主要配置属性的propertySources)对象的浅拷贝。在加载Environment相应资源时参数化成了下面3个变量。
·{application}:对应客户端配置中的spring.application.name。
·{profile}:对应客户端配置中的spring.profiles.active(多个profile使用逗号分开)。
·{label}:对应配置服务器端所配置的spring.cloud.config.label,如Git中的master。
例如客户端配置(这些配置一定是在bootstrap.properties文件或bootstrap. yml中):
spring.application.name=foo
spring.profiles.active=dev,mysql
配置服务器首先会通过application. properties(所有的客户端默认都会使用该配置)和foo.properties(foo.properties配置文件的优先级会更高一些)创建一个Environment对象。如果配置资源库中含有指定的Spring profiles(如存在foo-dev.properties或foo-mysql.properties)文件,那么这些profiles也将被加载到Environment中,并拥有更高的优先级(profiles之间则按照所配置的顺序)。
可以看到这种构建方式和单独使用Spring Boot构建系统时配置文件加载规则时是一致的。与Spring Boot应用程序一样,这些参数也可以通过环境变量或命令行参数进行设置。
配置客户端从配置服务器获取配置数据的整个流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0IaP3De-1589521005121)(C:\Users\zhangli\AppData\Roaming\Typora\typora-user-images\image-20200509173430281.png)]
(1)当配置客户端启动时,根据bootstrap.properties中配置的应用名称(spring. application.name)、环境名(spring.profiles.active),向配置服务器请求获取配置数据。(2)配置服务器根据客户端的请求参数,以及配置文件中所配置的标签(spring.cloud. config.label,如果没有配置,对于Git来说默认为master分支),从Git仓库中按照上述规则去查找符合的配置文件。(3)配置服务器将匹配到的Git仓库拉取到本地,并建立本地缓存。(4)配置服务器根据所拉取到的配置文件创建Spring的ApplicationContext实例,然后将该配置信息返回给客户端。(5)客户端获取到配置服务器返回的数据后,将这些配置数据加载到自己的上下文中。同时,因为这些配置数据的优先级高于本地JAR包中的配置,因此将不再加载本地的配置。
另外,当直接访问配置服务器的端点时,可以按照如下映射关系来匹配相应的配置文件:·/{application}/{profile}[/{label}]
·/{application}-{profile}.yml
·/{label}/{application}-{profile}.yml
·/{application}-{profile}.properties
·/{label}/{application}
-{profile}.properties
上面这些URL将会按照“{application}-{profile}.properties(yml)”格式匹配配置文件。label则是对应的Git上分支名称,是一个可选参数,如果没有,则默认从master分支进行查找。
7.3.2 集成Git仓库
spring.cloud.config.server.git.uri属性设置即可;
在配置Git仓库的路径时,也可以使用{application}、{profile}和{label}这些占位符来配置,可以为每一个应用客户端创建一个单独的仓库。
注意:如果Git的分支或标签中包含“/”时,在{label}参数中需要使用“_”替代,这是为了避免与HTTP URL转义符处理时的冲突。
也可以通过使用{application}/{profile}进行模式匹配,以便获取到相应的配置文件
# 匹配所有应用名称为simple
spring.cloud.config.server.git.repos.simple=https://github.com/simple/config-repo
# 匹配所有应用名称以special开头,并且环境名称以dev开头
# 或所有应用名称中含有special,并且环境名称以dev开头
spring.cloud.config.server.git.repos.special.pattern=special*/dev*, *special*/dev*
如果模式中需要配置多个值,那么可以使用逗号分隔。如果{application}/{profile}没有匹配到任何资源,则使用spring.cloud.config.server.git.uri配置的默认URI。
当我们使用yml类型的文件进行配置时,如果模式属性是一个YAML数组,也可以使用YAML数组格式来定义。这样可以设置成多个配置文件
config: server: git: uri:
repos: development: pattern: uri: staging: pattern:
7.3.3 搜索目录
如果配置文件直接放置在Git存储库的根目录下,则不需要进行配置。如果把配置文件存放在特定的子目录中时,那么可以通过设置searchPaths属性配置指定该目录。
spring.cloud.config.server.git.searchPaths=?,也支持通配符方式
7.3.4 本地缓存
配置服务器从Git仓库中获取配置信息后,将会在本地的文件系统中存储一份。默认将存储在系统临时目录下,并且以config-repo-作为开头。在Linux系统下的默认存储目录为/tmp/config-repo-。
为配置服务器指定明确的本地缓存路径:
spring.cloud.config.server.git/svn.basedir=?
7.3.5 Git访问配置
在访问Git仓库时,如果不想使用HTTPS和用户认证,那么可以直接使用SSH进行访问。这时只需要将SSH需要的keys存储在~/.ssh目录下即可,并将所配置的URI指向SSH地址即可,如[email protected]:cd826/SpringcloudSamplesConfig。如果读者不清楚自己的~/.git目录是在哪里,那么可以使用git config -global进行全局配置。
另外,Spring Cloud Config默认使用JGit访问Git仓库,如果你的HTTPS访问需要设置代理服务器,那么可以在~/.git/config下进行配置,但也可以通过JVM系统属性-Dhttps.proxyHost和-Dhttps.proxyPort来设置。
7.3.6 集成SVN
(1)修改配置服务器的依赖,删除原来对Git的依赖,修改为对SVN的依赖。


org.tmatesoft.svnkit
svnkit
(2)修改配置服务器配置,将原来的Git仓库的地址修改为SVN仓库的地址。application. properties文件修改如下:
```java
# 这里是配置文件所在的SVN仓库地址及访问用户的登录名、登录口令
spring.cloud.config.server.svn.uri={your svn server}
spring.cloud.config.server.svn.username=username
spring.cloud.config.server.svn.password=password
7.3.7 使用文件系统
在配置服务器的配置文件中添加spring.profiles.active=native配置属性
默认配置服务器是从classpath目录下加载配置文件,我们可以通过spring.cloud.config.server.native.searchLocations属性来设置配置文件所在的目录。对于所配置的文件路径,配置必须以“file:”开头。如果配置服务器的操作系统是Windows系统,那么对于绝对路径,还需要对其中的“/”进行转义。例如,file:///${user.home}/config-repo。
此外,spring.cloud.config.server.native.searchLocations属性的配置也支持使用{application}、{profile}和{label}占位符进行配置。
7.4 配置的加密与解密
Spring Cloud Config提供了两种加解密方式:对称加密和非对称加密。
7.4.1 安装JCE(Java CryptographyExtension)
Spring Cloud Config所提供的加解密依赖JCE(JavaCryptography Extension,即Java加密扩展)是一组包,提供用于加密、密钥生成算法和协议,以及消息认证码的算法及实现,这些包实现对称、非对称、块和流密码的加密支持。不过这些包在默认JDK中是不存在的,需要额外进行安装。安装方式也比较简单,步骤如下:
(1)从Oracle官方地址(http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html,针对于JDK8,该地址有可能改变)下载JCE扩展包jce_ policy-8.zip。(2)解压该压缩包,获取到两个jar包:local_policy.jar和US_export_policy.jar。(3)打开 J A V A H O M E / j r e / l i b / s e c u r i t y 目 录 , 并 将 该 目 录 下 的 l o c a l p o l i c y . j a r 和 U S e x p o r t p o l i c y . j a r 文 件 备 份 到 其 他 目 录 。 ( 4 ) 将 第 ( 2 ) 步 解 压 的 J A R 文 件 复 制 到 JAVA_HOME/jre/lib/security目录,并将该目录下的local_policy.jar和US_export_policy.jar文件备份到其他目录。(4)将第(2)步解压的JAR文件复制到 JAVAHOME/jre/lib/security目录,并将该目录下的localpolicy.jar和USexportpolicy.jar文件备份到其他目录。(4)将第(2)步解压的JAR文件复制到JAVA_HOME/jre/lib/security目录下。
7.4.2 使用对称加密
对对称加/解密的配置非常简单:设置配置文件加/解密所使用的密钥encrypt.key即可。最简单的方式就是在配置文件(bootstrap.properties)中进行配置
encrypt.key=cd826dong_ekey
单独设置某些项的加解密
如果我们使用的是properties格式的配置文件,那么在配置文件中只需要为所加密配置项的值之前增加一个{cipher}前导符即可。比如:
spring.datasource.password={dipher}kdufu8ufsfsfs9fd89fs8df
如果时yml格式配置文件,需要将上面加密内容用单引号引起来;
对不同的敏感信息使用不同的加密key,如foo.bar={cipher}{key:testkey}…
配置服务器在解密的时候会尝试从配置中获取testkey作为密钥进行解密。
7.4.3 加密/解密端点
·/encrypt:当使用Postman调用加密端点时需要将HTTP请求的方式设置为POST
·/decrypt: http://config-server:其端口/decrypt
那么配置客户端是否能够正确地获取到解密后的配置内容呢?下面我们先将更改后的配置文件提交到Git仓库中,并启动配置服务器,然后通过Postman访问http://localhost:8888/productservice/dev,已经起作用;
实际生产环境中,建议就是将密码存放在配置服务器的系统环境变量中。另外,千万不要忘记所设置的密码,一旦忘记该密码,将无法对已加密的内容进行解密与恢复,还有一点就是不要把数据库密码设置得太简单了。
7.4.4 客户端解密
以上配置服务的加解密,在客户端所获取的配置内容已经时经过服务器解密的内容,如何在客户端自己对加密配置项来解密呢?
(1)需要禁用掉配置服务端的解密处理。
在配置文件(bootstrap. properties/yml)中,spring.cloud.config.server.encrypt.enabled=false
(2)在客户端工程增加和服务器同样的密钥,encrypt.key=cd826dong_ekey
(3)为客户端工程增加对spring-security-rsa的依赖
使用Postman访问http://localhost:8888/productservice/dev,查看日志输出是已解密了的
7.4.5 非对称加密(安全性更高)
借助一个Java工具——Keytool。Keytool是一个Java数据证书的管理工具,Keytool使用keystore文件存储密钥和证书。keystore文件里包含两种数据:一种是密钥实体-密钥或者是私钥和配对公钥(采用非对称加密);另一种是可信任的证书实体,仅包含公钥。一个keystore文件中可存储多个条目,访问keystore文件和keystore文件中的条目时均需要密码。
要完成非对称加密的配置,首先需要借助Keytool生成一个密钥对,然后创建keystore文件。命令如下:
$ keytool -genkeypair -alias tsfjckey -keyalg RSA \
-dname "CN=SpringCloud Demo, OU=cd826dong, O=xohaa, L=gz, S=gd, C=china" \
-keypass javatwostepsfrom -keystore server.jks -storepass twostepsfromjava
然后将所生成的server.jks文件复制到配置服务器的resources目录下。最后修改配置文件(bootstrap.properties/yml):
encrypt.key-store.location=server.jks
encrypt.key-store.alias=tsfjckey
encrypt.key-store.password=twostepsfromjava
encrypt.key-store.secret=javatwostepsfrom
7.5 配置服务器访问安全
对于配置服务器的访问安全控制有很多种,如物理网络限制、OAuth2授权等。但是因为这里我们使用的是Spring Boot框架,所以使用Spring Security来做访问安全控制,更容易也更简单一些。
(1)添加依赖spring-boot-starter-security
(2)配置文件bootstrap.properties中,配置
security.user.name=cd826dong
security.suer.password=configPwd
注意:如果不配置用户名和密码的话,在配置服务器启动时,Spring Security会默认生成一个访问密码。
(3)重新启动配置服务器,访问http://localhost:8888/productservice/dev;
那么对于客户端的配置该怎么做呢?很简单,只需要在相应工程的配置文件中增加如下配置,客户端就可以正常去配置服务器上获取的配置数据了:
spring.cloud.config.username=cd826dong
spring.cloud.config.password=configPwd
7.6 配置服务器的高可用
7.6.1 整合Eureka
注意:上面商品微服务配置config.uri使用的是具体地址;
1.改造配置服务器
pom增加eureka依赖;
bootstrap.properties中配置服务名称和地址
spring.application.name=configserver
eureka.client.service-url.defaultZone=http://localhost:8260/eureka
启动类加@EnableDiscoveryClient
2.改造配置客户端——商品服务
spring.cloud.config.uri=http://configserver
7.6.2 快速失败与响应
在默认情况下,只有当客户端向配置服务发起请求时,配置服务器才会从配置仓库(如Git仓库)中加载配置文件。可以通过设置clone-on-start,让配置服务器在启动时就加载配置文件。
spring.cloud.config.server.git.repos.team-a.clone-on-start=true;
team-a小组所使用的配置文件,在配置服务器启动时就会加载
也可以通过设置spring.cloud.config.server.git.clone-on-start属性的值进行全局配置;
spring.cloud.config.fail-fase=true当无法连接到配置服务器时希望其能够快速返回失败
为配置客户端开启重试机制:
客户端添加spring-retry和spring-boot-starter-aop这两个依赖
客户端默认会尝试连接6次,时间间隔初始值为1000毫秒,后面每次尝试连接则会按照1.1倍数增加尝试连接时间的间隔,如果最后还不能连接到配置服务器才会返回错误。可以通过在配置文件中复写“spring.cloud.config.retry.*”开头的相关属性,来精确控制客户端重试的次数和时间间隔。
如果读者想实现自己的客户端重试机制,可通过继承RetryOperationsInterceptor的类,并实现客户端重试逻辑,然后将该实现注册为一个Bean,把ID设置为configServerRetry Interceptor即可。
7.6.3 动态刷新配置
在配置客户端中提供了一个refresh端点来实现配置文件的刷新。要想使用该功能,需要先在配置客户端中增加对spring-boot-starter-actuator的依赖。
当配置文件修改并提交到Git仓库后,就可以访问配置客户端的refresh端点(如http://localhost:8080/refresh)来更新本地的配置数据。但最好的方式还是和Spring CloudBus进行整合,这样才能实现配置的自动分发,而不需要手工一个个去刷新配置。
来精确控制客户端重试的次数和时间间隔。
如果读者想实现自己的客户端重试机制,可通过继承RetryOperationsInterceptor的类,并实现客户端重试逻辑,然后将该实现注册为一个Bean,把ID设置为configServerRetry Interceptor即可。
7.6.3 动态刷新配置
在配置客户端中提供了一个refresh端点来实现配置文件的刷新。要想使用该功能,需要先在配置客户端中增加对spring-boot-starter-actuator的依赖。
当配置文件修改并提交到Git仓库后,就可以访问配置客户端的refresh端点(如http://localhost:8080/refresh)来更新本地的配置数据。但最好的方式还是和Spring CloudBus进行整合,这样才能实现配置的自动分发,而不需要手工一个个去刷新配置。