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. servlet执行

    WEB编程阶段 在WEB编程中,我们可以通过浏览器访问WEB服务器上的资源.WEB服务器就是另一台计算机 浏览器的入口不同(访问路径),访问的资源也不同. 为了灵活实现的不同路径(/hello)执行不 ...

  2. Day02_CMS前端开发

    CMS前端开发 1 Vue.js与Webpack研究 参考链接:https://www.cnblogs.com/artwalker/p/13387386.html 2 CMS前端工程创建 2.1 导入 ...

  3. flask的第一次尝试

    from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return "Hello ...

  4. python基础day6_字典dict

    数据类型划分:可变数据类型.不可变数据类型 不可变数据类型(又叫可哈希):元祖,bool ,int,str, 可变数据类型(又叫不可哈希):list,dict,set(集合) dict的key必须是不 ...

  5. PHP date_default_timezone_get() 函数

    ------------恢复内容开始------------ 实例 返回默认时区: <?phpecho date_default_timezone_get();?> 运行实例 » 定义和用 ...

  6. PHP dechex() 函数

    实例 把十进制转换为十六进制: <?phpecho dechex("30") . "<br>";echo dechex("10&qu ...

  7. 探究:编程语言那么多,为什么偏偏是 C 语言成了大学的必修课?

    谁叫你不幸生在中国了? ——何祚庥(中国科学院院士) 这是一本给非计算机专业的大学生的C语言的书.“我不是学计算机的,为啥要学C语言?”这个问题每年在中华大地都会被问上几百万次.被问的对象可能是老师, ...

  8. stm32f407 oled iic例程,成功点亮oled屏

    写了好久好久,写好多好多代码,终于把oled给驱起来了.话不多说,直接上图,欲要例程的,可以加我微 lichenpoo

  9. 银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它

    银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它 Hello~everybody!小V又来咯!这次小V给大家带来的是零代码开发V平台常用的时间日期函数.小V知道我们平时常常会 ...

  10. “随手记”开发记录day02

    今天完成了 向瑜- 布局: 1.修改日期(√) 2.选择分类(√) 3.输入金额(√) 赵常恒- 1.登录,注册页面布局(√) 刘志霄- 1.个人信息页面规划(√)