1 前言

欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章!

可配置是一个成熟软件系统应该提供的特性,而配置管理对于大型系统就显得十分重要,特别是对于拥有多个应用的微服务系统。可喜的是,Spring为我们提供了很好的配置管理,如Springboot的配置就很强大。对于Spring Cloud,就有强大的Spring Cloud Config,在提供了一个在应用之外的配置管理,如文件或Git仓库,对分布式系统配置管理十分有益。

2 快速体验

Spring Cloud Config服务端就是一个Springboot应用,启动、部署都十分简单。

整体的架构如下图所示:

2.1 服务端就是一个Springboot

Springboot中添加依赖如下:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>

只需要一个就行了,它已经包含了webactuator

添加Java主类:

package com.pkslow.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class,args);
}
}

跟普通的Springboot应用相比只是多了一个注解@EnableConfigServer而已。

2.2 配置仓库

通过我们会把配置通过版本控制管理起来,一般使用Git仓库,为简单展示使用本地仓库如下:

# 创建目录
mkdir git-repo # 初始化一个git目录
git init # 新建文件
touch application.properties # 添加变更
git add . # 提交变更
git commit -m "init"

配置一下项目的application.properties,注意是Config Server项目的,而不是在git-repo目录里的:

server.port=8888
spring.application.name=config-server
spring.cloud.config.server.git.uri=/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo

接着就可以启动Config Server了。

git仓库里的配置文件没有什么内容,我们加入以下内容并提交(必须要提交,不然无法获取)。

pkslow.webSite=www.pkslow.com
pkslow.age=18
pkslow.email=admin@pkslow.com

2.3 配置路径匹配

那我们如何获取这些配置呢?可以通过以下URL读取:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
  • label指的是代码分支,如masterfeature-1等。

  • application是应用的名字,在以后客户端读取会用到。

  • profile一般用于指定环境,如proddevuat等。

所以,我们可以用以下URL来获取我们刚添加的配置信息:

http://localhost:8888/application/default
http://localhost:8888/application/default/master
http://localhost:8888/master/application.properties
http://localhost:8888/application-default.properties

访问如下:

$ curl http://localhost:8888/application/default/master
{"name":"application","profiles":["default"],"label":"master","version":"8796f39b35095f6e9b7176457eb03dd6d62b1783","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.age":"18","pkslow.email":"admin@pkslow.com"}}]}

最后一个地址/{label}/{application}-{profile}.properties的返回结果格式不同,直接返回配置文件内容:

$ curl http://localhost:8888/application-default.properties
pkslow.age: 18
pkslow.email: admin@pkslow.com
pkslow.webSite: www.pkslow.com

如果我们先建一个分支release-20200809,并修改age为实际年龄9,则如下:

$ curl http://localhost:8888/application/default/release-20200809
{"name":"application","profiles":["default"],"label":"release-20200809","version":"7e27e6972ed31ee1a51e9277a2f5c0a628cec67a","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.age":"9","pkslow.email":"admin@pkslow.com"}}]}

可以看到对应的pkslow.age已经变为9了,但访问/application/default/master则还是18,分支之间不会相互影响。

2.4 远程仓库

本地仓库只是为了简单展示,在实际项目中,一般使用远程仓库,在GitHub创建一个新的仓库如下:

特意创建了个private的仓库来检测后面的鉴权是否正确。

重新配置仓库的地址如下:

spring.cloud.config.server.git.uri=https://github.com/pkslow/pkslow-config
spring.cloud.config.server.git.username=admin@pkslow.com
spring.cloud.config.server.git.password=***
spring.cloud.config.server.git.default-label=master
spring.cloud.config.server.git.search-paths=demo

创建一个demo目录来放置配置,所以search-paths配置为demo。完成配置重启服务器,就可以正常读取远程仓库的配置了。

2.5 多个代码配置仓库

有些时候,我们的配置可能并不只在一个仓库里,而是在各自客户端的代码库中,比如我们有以下三个服务:

  • (1)服务发现:discovery,代码库pkslow-discovery-service
  • (2)API网关:gateway,代码库pkslow-gateway-service
  • (3)订单服务:order,代码库pkslow-order-service

它们各自的配置文件都放在各自的代码库里,那就需要配置多个代码库。我们还多配置一个默认的配置库pkslow-default,如果匹配不到,就会选择默认代码库的配置。具体配置如下:

server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: /Users/pkslow/multiple-repos/pkslow-default
repos:
pkslow-discovery-service:
pattern: pkslow-discovery-*
cloneOnStart: true
uri: /Users/pkslow/multiple-repos/pkslow-discovery-service
search-paths: config
pkslow-gateway-service:
pattern: pkslow-gateway-*/dev
cloneOnStart: true
uri: /Users/pkslow/multiple-repos/pkslow-gateway-service
search-paths: config
pkslow-order-service:
pattern: pkslow-order-*
cloneOnStart: true
uri: /Users/pkslow/IdeaProjects/pkslow-order-service
search-paths: config

可以各自定义配置文件所放的目录search-paths,不配置默认为根目录。

这里的pattern的配置规则是{application}/{profile},支持正则符号*。注意只匹配一个结果,如果都满足,只取第一个匹配的仓库。

启动后我们来看看配置结果:

# 默认profile和label,正确匹配
$ curl http://localhost:8888/pkslow-order-service/default/master
{"name":"pkslow-order-service","profiles":["default"],"label":"master","version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"order-service"}}]} # 正确匹配,但不存在的Label,配置库没有对应代码分支,404
$ curl http://localhost:8888/pkslow-order-service/default/release
{"timestamp":"2020-08-13T06:58:38.722+0000","status":404,"error":"Not Found","message":"No such label: release","path":"/pkslow-order-service/default/release"} # profile为dev,正确匹配
$ curl http://localhost:8888/pkslow-order-service/dev/master
{"name":"pkslow-order-service","profiles":["dev"],"label":"master","version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"order-service"}}]} # 对于gateway只能匹配profile=dev,无法匹配,读取默认配置
$ curl http://localhost:8888/pkslow-gateway-service/default/master
{"name":"pkslow-gateway-service","profiles":["default"],"label":"master","version":"8358f2b4701fac21a0c7776bc46cec6d9442c549","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties","source":{"pkslow.birthDate":"2020-08-10"}}]} # 对于gateway只能匹配profile=dev,正确匹配
$ curl http://localhost:8888/pkslow-gateway-service/dev/master
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"master","version":"1a4e26849b237dc2592ca0d391daaa1a879747d2","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"www.pkslow.com","pkslow.app.name":"gateway-service"}}]} # 不存在的服务名,无法匹配,读取默认配置
$ curl http://localhost:8888/unknown-service/dev/master
{"name":"unknown-service","profiles":["dev"],"label":"master","version":"8358f2b4701fac21a0c7776bc46cec6d9442c549","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties","source":{"pkslow.birthDate":"2020-08-10"}}]}

3 客户端使用配置

经过前面的例子我们已经了解到服务端如何从代码库里获取配置,但始终还是要使客户端能获取到对应的配置并产生效果。我们来演示一下。

3.1 项目准备

搭建一个最简单的Springboot Web项目,并加上Spring Cloud Config的支持,添加依赖如下:

<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>

添加配置文件:bootstrap.properties(虽然我们要从服务端读取配置,但有些配置还是必须在客户添加的,如服务端地址),内容如下:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.cloud.config.uri=http://localhost:8888

在这里我们配置了客户端的端口、服务端的地址以及客户端应用的名字,这个名字是非常关键的,待会讲解。

添加Controller来暴露API以显示读到的配置内容:

@RestController
public class PkslowController {
@Value("${pkslow.age}")
private Integer age; @Value("${pkslow.email}")
private String email; @Value("${pkslow.webSite}")
private String webSite; @GetMapping("/pkslow")
public Map<String, String> getConfig() {
Map<String, String> map = new HashMap<>();
map.put("age", age.toString());
map.put("email", email);
map.put("webSite", webSite);
return map;
}
}

接着启动客户端即可,访问结果如下:

$ curl http://localhost:8080/pkslow
{"webSite":"default.pkslow.com","age":"9","email":"admin@pkslow.com"}

这些配置内容并不在客户端,说明可以从服务端获取到配置信息了。

3.2 客户端如何匹配

客户端已经获取到配置信息了,那是否正确呢?客户端又是怎样匹配的呢?其实前面的内容已经提及,主要通过三个信息来匹配:

  • label指的是代码分支。

  • application是应用的名字。

  • profile一般用于指定环境。

上个例子客户端名字为pkslow-gateway-servicelabel没指定,默认为masterprofile没有指定,默认为default

我们在服务端匹配规则为pattern: pkslow-gateway-*/dev,所以可以匹配名字,但无法匹配profile,因此读取了默认仓库的配置:

$ cat pkslow-base/application.properties
pkslow.webSite=default.pkslow.com
pkslow.age=9
pkslow.email=admin@pkslow.com

修改一下客户端的配置,配置profiledev如下:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.uri=http://localhost:8888

再次获取客户端的配置如下:

$ curl http://localhost:8080/pkslow
{"webSite":"gateway-master.pkslow.com","age":"9","email":"admin@pkslow.com"}

可见已经读取到pkslow-gateway-service仓库的配置内容了:

$ cat pkslow-gateway-service/config/application.properties
pkslow.webSite=gateway-master.pkslow.com
pkslow.age=9
pkslow.email=admin@pkslow.com

pkslow-gateway-service仓库新建代码分支release并添加配置,再修改客户端配置如下:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.label=release
spring.cloud.config.uri=http://localhost:8888

重启后再次访问,正确读取到了新分支的配置:

$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"9","email":"admin@pkslow.com"}

3.3 客户端配置生效问题

当我们修改配置后,再次访问结果如下:

$ git commit -a -m "update"
[release 0e489fe] update
1 file changed, 2 insertions(+), 2 deletions(-) $ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"release","version":"0e489fec5de73b1a6d11befa3f65e44836979e23","state":null,"propertySources":[{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"gateway-release.pkslow.com","pkslow.age":"10","pkslow.email":"admin@pkslow.com"}}]} $ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"9","email":"admin@pkslow.com"}

结果发现,服务端已经生效了,但客户端并没有。这是因为在这种模式下客户端只会在启动的时候读取配置使其生效。如果想要客户端也生效,我们要使用Springboot actuator提供的/refresh端点才行。

先添加actuator依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置要保证/refresh可以访问,添加如下配置:

management.endpoints.web.exposure.include=refresh

指定刷新的范围,在Controller上添加注解@RefreshScope如下:

@RefreshScope
@RestController
public class PkslowController {
//xxx
}

重启应用。操作及效果如下:

# 修改配置并提交
$ git commit -a -m "update age to 18"
[release fc863bd] update age to 18
1 file changed, 1 insertion(+), 1 deletion(-) # 服务端配置生效
$ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service","profiles":["dev"],"label":"release","version":"fc863bd8849fa1dc5eaf2ce0a97afb485f81c2f0","state":null,"propertySources":[{"name":"/Users/larry/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.properties","source":{"pkslow.webSite":"gateway-release.pkslow.com","pkslow.age":"18","pkslow.email":"admin@pkslow.com"}}]} # 客户端没有生效
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"10","email":"admin@pkslow.com"} # 发送POST请求到客户端/refresh
$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version","pkslow.age"] # 客户端已经生效
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com","age":"18","email":"admin@pkslow.com"}

3.4 自动更新配置

每次提交代码配置后,都需要手动发一次POST请求到客户端才能更新配置,显然是不够友好的。GitHub提供了webhook功能,可以在触发事件后,发送请求到指定URL,这样便可以实现自动更新了。

通过webhook功能实现自动更新是一对一的,如果客户端很多(通常是这种场景),就无法这样直接实现。有两个方案:

(1)自己实现一个端口来接受来自Git的请求,然后再分发到各个服务端。这个方法比较麻烦,不是很推荐。

(2)通过引入Spring Cloud Bus来刷新多个客户端。但需要引入MQ,如kafkaRabbitMQ

3.5 在有服务发现时的不同

在微服务架构中,如果配置服务端与客户端都注册在服务发现(如eureka)上时,客户端就无须再配置服务端的地址了,会从服务发现中心获取识别。这与Springboot Admin在有eureka的情况下有异曲同工之妙。

代码没什么特别之外,就是把服务端和客户端同时注册到eureka即可。

4 总结

本文通过例子一步步展示如何使用Spring Cloud Config,主要是理解交互过程和匹配规则,其它都是代码细节,参考官方文档即可。

关于配置的一些文章:

Java怎么从这四个位置读取配置文件Properties(普通文件系统-classpath-jar-URL)

注解@ConfigurationProperties让配置整齐而简单

只想用一篇文章记录@Value的使用,不想再找其它了

Springboot整合Jasypt,让配置信息安全最优雅方便的方式

使用Spring Cloud Config统一管理配置,别再到处放配置文件了

Spring Cloud Config整合Spring Cloud Kubernetes,在k8s上管理配置


欢迎关注微信公众号<南瓜慢说>,将持续为你更新...

多读书,多分享;多写作,多整理。

使用Spring Cloud Config统一管理配置,别再到处放配置文件了的更多相关文章

  1. .NET Core微服务之基于Steeltoe使用Spring Cloud Config统一管理配置

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 =>  Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...

  2. 9.Spring Cloud Config统一管理微服务配置

    Spring Cloud Config统一管理微服务配置 9.1. 为什么要统一管理微服务配置 9.2. Spring Cloud Config简介 Spring Cloud Config为分布式系统 ...

  3. 【SpringCloud构建微服务系列】使用Spring Cloud Config统一管理服务配置

    一.为什么要统一管理微服务配置 对于传统的单体应用而言,常使用配置文件来管理所有配置,比如SpringBoot的application.yml文件,但是在微服务架构中全部手动修改的话很麻烦而且不易维护 ...

  4. 【Spring Cloud】Spring Cloud Config 实现分布式配置中心

    Spring Cloud Config 实现分布式配置中心 一.分布式配置中心 分布式系统中,往往拥有大量的服务应用,而每个应用程序都需要有对应的配置文件来协助完成服务环境初始化.运行.因此生产了大量 ...

  5. SpringCloud实战之初级入门(三)— spring cloud config搭建git配置中心

    目录 1.环境介绍 2.配置中心 2.1 创建工程 2.2 修改配置文件 2.3 在github中加入配置文件 2.3 修改启动文件 3. 访问配置中心 1.环境介绍 上一篇文章中,我们介绍了如何利用 ...

  6. spring cloud config svn仓库配置

    之前快速入门了一下spring cloud config 但是仓库用的别人博客上的git仓库,公司用的是svn项目管理中心,下面这个自己配置的时候出现的错误 You need to configure ...

  7. spring cloud config搭建说明例子(四)-补充配置文件

    服务端 ConfigServer pom.xml <dependency> <groupId>org.springframework.cloud</groupId> ...

  8. Spring Cloud Config整合Spring Cloud Kubernetes,在k8s上管理配置

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Kubernetes有专门的ConfigMap和Secret来管理配置,但它也有一些局限性,所以还是希望通过Spring C ...

  9. springCloud学习-分布式配置中心(Spring Cloud Config)

    1.简介 Spring Cloud Config :分布式配置中心,方便服务配置文件统一管理,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中.在spring cloud co ...

随机推荐

  1. python学习笔记1 -- 函数式编程之高阶函数 map 和reduce

    我用我自己,就是高阶函数,直接表现就是函数可以作为另一个函数的参数,也可以作为返回值 首先一个知识点是 函数的表现形式,印象中的是def  fw(参数)这种方式定义一个函数 python有很多的内置函 ...

  2. leetcode 5473

    这个题真是当时想麻烦了,,,感谢LLdl 提供的题解 其实一个很重要的点就是,如果后面的玩意翻转了偶数次,那就跟没变一样.如果是奇数次就取反. 怪我天真,第一反应就去位运算去了,,,,哪有那么复杂诶 ...

  3. Fortify Audit Workbench 笔记 Unreleased Resource: Database( 未释放资源:数据库)

    Unreleased Resource: Database 未释放资源:数据库 Abstract 程序可能无法成功释放某一项系统资源. Explanation 程序可能无法成功释放某一项系统资源. 资 ...

  4. ROS 机器人技术 - 广播与接收 TF 坐标

    上次我们学习了 TF 的基本概念和如何发布静态的 TF 坐标: ROS 机器人技术 - TF 坐标系统基本概念 ROS 机器人技术 - 静态 TF 坐标帧 这次来总结下如何发布一个自定义的 TF 坐标 ...

  5. Python环境搭建及中文编码

    Python 官方下载地址:https://www.python.org/downloads/ 64 位下载客户端  32 位下载客户端 在安装之后,可能在桌面上看不到图标.这时可以到开始菜单下寻找 ...

  6. PHP ftp_ssl_connect() 函数

    定义和用法 ftp_ssl_connect() 函数打开一个安全的 SSL-FTP 连接. 当连接打开,您就可以在服务器运行 FTP 函数. 语法 ftp_ssl_connect(host,port, ...

  7. map进程数量和reduce进程数量

    1-map task的并发数量是由切片的数量决定的,有多少个切片就有启动多少个map task: 2-切片是一个逻辑的概念,指的是文件中数据的偏移量范围: 3-切片的具体大小应该根据所处理的文件大小来 ...

  8. Elasticsearch入门指南

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 这篇文章主要是记录一下最近在学的 ...

  9. K近邻算法(一)

    K 近邻算法思想: 寻找该点周围最近的K个点.根据这K 个点的类别来判断该点的类别: 核心: 数据归一化.(在必要的时候必须进行数据归一化处理,防止某一特征在计算数据时占比较重) 计算欧拉距离 . 使 ...

  10. 使用Flask开发简单接口(1)--GET请求接口

    前言 很多想学习接口测试的同学,可能在最开始的时候,常常会因没有可以练习的项目而苦恼,毕竟网上可以练习的接口项目不多,有些可能太简单了,有些可能又太复杂了,或者是网上一些免费接口请求次数有限制,最终导 ...