共计 11982 个字符,预计需要花费 30 分钟才能阅读完成。
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>
只须要一个就行了,它曾经蕴含了 web
和actuator
。
增加 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
指的是代码分支,如master
,feature-1
等。application
是利用的名字,在当前客户端读取会用到。profile
个别用于指定环境,如prod
,dev
,uat
等。
所以,咱们能够用以下 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-service
;label
没指定,默认为 master
;profile
没有指定,默认为default
。
咱们在服务端匹配规定为pattern: pkslow-gateway-*/dev
,所以能够匹配名字,但无奈匹配profile
,因而读取了默认仓库的配置:
$ cat pkslow-base/application.properties
pkslow.webSite=default.pkslow.com
pkslow.age=9
pkslow.email=admin@pkslow.com
批改一下客户端的配置,配置 profile
为dev
如下:
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
,如kafka
或RabbitMQ
。
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 上治理配置
欢送关注微信公众号 <南瓜慢说>,将继续为你更新 …
多读书,多分享;多写作,多整顿。