本系列文章的写作环境为:
Spring Boot 2.0.3.RELEASE
、Spring Cloud Finchley.RELEASE
、JDK 8
、IDEA
先说一下最近的状态吧:2016年毕的业,7月4号参加的工作,到今天整好2年。也正是职业生涯开始迷茫和遇到瓶颈的时间。平常很喜欢看技术类资料,也关注了很多好的博客和微信公众号。每天都可以看到很多技术文章,好的有烂的也有,也花费了不少精力看这些文章,但是有很多文章可以说是重复的在看(同样一个技术点,不同的文章写的重点不一样,把这些“重复”的文章组合起来也才是这个技术点70%的内容,并且也形不成一个体系),所以很大一部分精力是白白浪费了
。最近 Spring 系列(Spring 5、Spring Boot、SpringCloud)的技术很火,也开始着手研究这些东西。但是,不想再看那些二手资料了,所以开始阅读官方文章进行学习,说实话,对于一个四级没有过的人来说实在是太痛苦了(都是泪啊)。不过,强迫自己阅读了两天官方文档,现在也没有那么痛苦了,还是需要慢慢的习惯。同时也让我觉得再也不想看那些二手资料了(二手的东西都太垃圾了)。本篇文章就是通过阅读官方文档写作完成(当然,也参考了其他的文章,一方面经验尚浅理解不够深入,另一方也把别人好的东西借鉴过来)
。
我相信很多小伙伴也有和我一样的情况吧。好了,不说废话了,直接上干货。
Spring Cloud Config 介绍
Spring Cloud Confi
是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端
与客户端
两个部分。其中服务端也称为分布式配置中心
,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息、加密/解密信息等访问接口;而客户端则是微服务架构中的各个微服务应用或基础设施
,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。Spring Cloud Config
实现了对服务端和客户端中环境变量和属性配置的抽象映射,所以它除了适用于 Spring 构建的应用程序之外,也可以在任何其他语言运行的应用程序中使用。由于Spring Cloud Config
实现的配置中心默认采用 Git 来存储配置信息,所以使用Spring Cloud Config
构建的配置服务器,天然就支持对微服务应用配置信息的版本管理,并且可以通过 Git 客户端工具来方便的管理和访问配置内容。当然它也提供了对其他存储方式的支持,比如:SVN仓库、本地化文件系统、数据库等。
在本文中,我们将学习如何构建一个基于 Git 存储的分布式配置中心,并对客户端进行改造,并让其能够从配置中心获取配置信息并绑定到代码中的整个过程。
Spring Cloud Config 工作过程
- 首先我们需要一个远程的 Git 仓库,自己学习可以直接用 GitHub 或码云,但在实际生产环境中,需要自己搭建一个 Git 服务器。远程 Git 仓库的作用主要是用来保存我们的配置文件;
- 除了远程 Git 仓库之外,我们还需要一个本地 Git 仓库,每当 Config Server 访问远程 Git 仓库时,都会保存(clone)一份到本地,这样当远程仓库无法连接时,就直接使用本地存储的配置信息;
- 至于微服务A、微服务B则是我们具体的应用,这些应用在启动的时候会从 Config Server 中来加载相应的配置信息;
- 当微服务A/B尝试去从 Config Server 中加载配置信息的时候,Config Server会先通过
git clone
命令克隆一份配置文件保存到本地(也可以设置成在 Config Server 启动时 clone 一份到本地); - 由于配置文件是存储在 Git 仓库中,所以配置文件天然的具备版本管理功能,Git 中的 Hook 功能可以实时监控配置文件的修改。
Spring Cloud Config Server 配置过程(配置中心)
Config Server 是通过使用 @EnableConfigServer
注解内嵌在 Spring Boot 应用中的,所以首先要创建一个 Spring Boot 应用。
1、pom.xml 添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding
<project.reporting.outputEncoding>UTF-8</project.reporting.output
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- 配置服务器依赖的jar包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 用于我们自己监控应用信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 统一版本号 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2、开启 Spring Cloud Config 的服务端功能
启动类上添加 @EnableConfigServer
注解,表示开启配置中心服务端功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
3、添加配置信息
application.yml
# 指定监听端口
server.port: 8090
# 加载所有的端点。默认只加载了 info / health
management:
endpoints:
web:
exposure:
include: "*"
bootstrap.yml
spring.application.name : config-server
spring:
cloud:
config:
server:
git:
uri: https://gitee.com/liupeifeng3514/config-repo.git
search-paths : configClient01
username : xxxxxx@126.com
password : yyyyyyy
overrides:
foo: bar
注意:对于 Git 中仓库和配置文件的匹配方式还有很多种方式,有兴趣的小伙伴可以看一下官方文档:http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html#_pattern_matching_and_multiple_repositories
配置解释:
spring.application.name
:配置应用名称(有注册中心时用于对应用进行标识),这里暂时没有什么用处;spring.cloud.config.server.git.uri
:git仓库的地址,注意,在新版本中地址的形式要使用以.git
结尾的;spring.cloud.config.server.git.search-paths
:仓库下的子目录,用于指定配置文件的位置;spring.cloud.config.server.git.username
:git服务器的用户名;spring.cloud.config.server.git.password
:git服务器的密码;- 其他配置参数
4、Git 仓库
这里我们使用 码云 作为 Git 仓库。目录如下:
config-repo
│ configClient-test.yml
│ configClient.yml
│
└─configClient01
configClient01-test.yml
configClient01.yml
到这里配置服务器就可以运行了,我们来测试一下试试。
5、测试配置服务器
完成了这些准备工作之后,我们就可以通过浏览器
、POSTMAN
或CURL
等工具直接来访问到我们的配置内容了。访问配置信息的URL与配置文件的映射关系如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
上面的 url 会映射{application}-{profile}.properties
对应的配置文件,其中{label}
对应 Git 上不同的分支,默认为master
。我们可以尝试构造不同的 url 来访问不同的配置内容,比如,要访问 master
分支,configClient01
应用的 test
环境,就可以访问这个 url:http://localhost:8090/configClient01/test/master,并获得如下返回:
{
"name": "configClient01",
"profiles": [
"test"
],
"label": "master",
"version": "4204c98e39e72f80fb6d4dffbacd8e991e21d8c8",
"state": null,
"propertySources": [
{
"name": "overrides",
"source": {
"foo": "bar"
}
},
{
"name": "https://gitee.com/liupeifeng3514/config-repo.git/configClient01/configClient01-test.yml",
"source": {
"test_in": "test_in_01",
"in": "test_in_01"
}
},
{
"name": "https://gitee.com/liupeifeng3514/config-repo.git/configClient01/configClient01.yml",
"source": {
"default_in": "default_in_01",
"in": "default_in_01"
}
}
]
}
可以看到该 Json 中返回了应用名:configClient01,环境名:test,分支名:master,以及 default 环境和 test 环境的配置内容。
再来进行一个测试
:
$ curl http://localhost:8090/configClient01-test.yml
default_in: default_in_01 # Git 仓库中 configClient01.yml 中的内容
foo: bar # Config Server 上 bootstrap.yml 中 配置的属性
in: test_in_01 # Git 仓库中 configClient01.yml 和 configClient01-test.yml 都有的内容,优先使用 configClient01-test.yml 中的内容
test_in: test_in_01 # Git 仓库中 configClient01-test.yml 中的内容
其实到这里就可以了,下面属于扩展学习了。接下文《Spring Cloud 探索 | 分布式配置中心(Config Client)》,两篇文章合起来才算是一个完整的实验。
安全保护
开发环境中我们的配置中心肯定是不能随随便便被人访问的,我们可以加上适当的保护机制,由于微服务是构建在 Spring Boot 之上,所以整合 Spring Security 是最方便的方式。
首先添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后在 application.yml 中配置用户名密码:
spring:
security:
user:
name: lpf
password: 123
最后在配置中心的客户端上配置用户名和密码即可,如下:
spring.cloud.config.username=lpf
spring.cloud.config.password=123
OK,如此之后,其他人就不能随意的获取到我们的配置信息了。
Git URI 中的占位符
灵活的使用 URI 占位符,可以有效的减少我们的工作量。考虑这样一个问题,我有 ServerA、ServerB 两个服务,两个服务对应的配置文件的存储地址分别位于 https://github.com/lenve/scConfig/sa 和 https://github.com/lenve/scConfig/sb,但是我的 Config Server 只有一个,那么当我的 ServerA 和 ServerB 连接上 Config Server 时,Config Server 怎么知道去哪个地址下拿配置文件?这个时候就涉及到占位符的使用。
在前面我们已经了解了Spring Cloud Config中的三种占位符,分别是 {application}、{profile} 和 {label},这些占位符除了用来标识配置文件的规则,还可以用在 Config Server 中对 Git 仓库的 URI 配置,用在 URI 配置中时,这三个占位符的含义分别如下所示:
- {application} 映射到客户端的 spring.application.name;
- {profile} 映射到客户端上的 spring.profiles.active;
- {label} 这是一个服务器端功能,标记”版本”的配置文件集。
此时,假设我不同环境下的配置文件分别放在下面这些目录下:
- https://github.com/lenve/scConfig/app/dev
- https://github.com/lenve/scConfig/app/prod
- https://github.com/lenve/scConfig/app/test
那么我的客户端文件这样配置:
spring.application.name=app
# dev根据具体情况来修改
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:2007/
server.port=2008
然后Config Server按下面这种方式配置即可:
spring.cloud.config.server.git.uri=https://github.com/lenve/scConfig.git
spring.cloud.config.server.git.search-paths={application}/{profile}
注意:在yaml文件中软使用占位符是要加单引号:
'{application}'
当然这种存储规划不一定最佳,这里只是给小伙伴们演示占位符的用法。
加密解密
没想到这篇文章写了这么多,加解密部分就放到下篇文章来说明吧。
后记:本来想写一篇文章来介绍加解密的,可是在实验的过程中出现了一个问题,就是对称加密的时候,在解密时总是失败,出现一下错误:
{"description":"Text not encrypted with this key","status":"INVALID"}
Caused by: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
尝试了半天也没找到解决办法。也就不浪费时间,还得继续向下研究呢。
附:其他配置参数(此部分可略过不看)
spring.cloud.config.server.git.basedir
:默认情况下,Config Server 克隆下来的文件保存在C:\Users\<当前用户>\AppData\Local\Temp
目录下,我们可以通过此配置来修改存放位置;spring.cloud.config.server.git.skipSslValidation
:配置服务器(Config Server)默认情况下会对 Git 服务器的 SSL 证书进行验证。可以通过设置为 true 来禁止验证。默认值为 false;spring.cloud.config.server.git.timeout
:配置服务器等待获取 HTTP 连接的最长时间,单位:秒;spring.cloud.config.server.git.force-pull
:当本地仓库(远程Git仓库的副本)被意外修改时,强制从远程Git仓库重新拉取数据;spring.cloud.config.server.git.deletedRemoteBranch
:由于 Spring Cloud Config Server 在检出分支到本地仓库后(例如通过 label 获取 properties)就拥有了远程 git 存储库的克隆,它将永久保留此分支或直到下一次服务器重新启动(这会创建新的本地仓库)。所以可能会出现这样的情况:远程分支被删除,但它的本地副本仍然可用于获取。如果 Spring Cloud Config Server 客户端服务以--spring.cloud.config.label=deletedRemoteBranch,master
开头,那么 server 将从 deletedRemoteBranch 本地分支 fetch 属性,而不是从 master 获取属性。为了保持本地存储库分支清洁并和远程分支保持一致,可以设置 deleteUntrackedBranches 属性。这将使 Spring Cloud Config Server 强制从本地存储库中删除未 trace 的分支;spring.cloud.config.server.XXXX.order
:当配置多个仓库的时候,可以通过此参数配置仓库的优先级,数值越小,优先级越高。例如:
spring:
profiles:
active: git, vault
cloud:
config:
server:
git:
uri: file:///path/to/git/repo
order: 2
vault:
host: 127.0.0.1
port: 8200
order: 1
spring.cloud.config.server.overrides
:在 Config Server 为所有的应用提供默认值。如下,所有的应用都可以读到foo=bar
;
spring:
cloud:
config:
server:
overrides:
foo: bar
spring.cloud.config.overrideNone
:待测试;spring.cloud.config.server.health
:默认情况下 Spring Cloud Config 会为配置中心服务端创建一个健康监测器,该检测器默认情况下访问的仓库文件是 {application} 为 app 的配置文件,如果仓库中不存在这个文件,健康显示器就会显示仓库无法连接,此时我们有两种解决方案:1.仓库中添加相应的配置文件;2.重新指定检测的配置,重新指定方式如下:
spring:
cloud:
config:
server:
health:
repositories:
configClient01: # 第一种方式。configClient01.yml
label: master
configClient01-test: # 第二种方式。configClient01-test.yml
name: configClient01
profiles: test
此时,系统会去访问 http://localhost:8090/configClient01/test/master 地址,如果能够访问到,则显示仓库已连接,如下:
spring.cloud.config.server.health.enabled
:设置为 false,关闭健康指示器;
源码地址:https://gitee.com/liupeifeng3514/Spring-Cloud-Learning
测试仓库地址:https://gitee.com/liupeifeng3514/config-repo