javascript
Spring Cloud Config 配置中心实践过程中,你需要了解这些细节!
本文導(dǎo)讀:
Spring Cloud Config 基本概念
Spring Cloud Config 客戶端加載流程
Spring Cloud Config 基于消息總線配置
Spring Cloud Config 中的占位符
Spring Cloud Config 倉(cāng)庫(kù)最佳實(shí)踐
Spring Cloud Config 健康檢查問(wèn)題剖析
本文主要介紹 Spring Cloud Config 基本概念、實(shí)踐過(guò)的配置及遇到的問(wèn)題進(jìn)行剖析。關(guān)于如何啟動(dòng)運(yùn)行配置中心可以參考官方 Demo。
本文基于?Spring Cloud?Edgware.SR3 版本實(shí)踐。
Spring Cloud Config 基本概念Spring Cloud Config 用來(lái)為分布式系統(tǒng)中的基礎(chǔ)設(shè)施和微服務(wù)應(yīng)用提供集中化的外部配置支持。
服務(wù)端:分布式配置中心,獨(dú)立的微服務(wù)應(yīng)用,用來(lái)連接配置倉(cāng)庫(kù)(GIT)并為客戶端提供獲取配置信息、加密/解密等訪問(wèn)接口。
客戶端:微服務(wù)架構(gòu)中各個(gè)微服務(wù)應(yīng)用和基礎(chǔ)設(shè)施,通過(guò)指定配置中心管理應(yīng)用資源與業(yè)務(wù)相關(guān)的配置內(nèi)容,啟動(dòng)時(shí)從配置中心獲取和加載配置信息
SCC作用:
實(shí)現(xiàn)了對(duì)服務(wù)端和客戶端中環(huán)境變量和屬性配置的抽象映射。
SCC優(yōu)勢(shì):
默認(rèn)采用 GIT 存儲(chǔ)配置信息,天然支持對(duì)配置信息的版本管理。
Spring Cloud Config架構(gòu)圖:
如上圖所示,架構(gòu)圖中的幾個(gè)主要元素作用: 遠(yuǎn)程 GIT 倉(cāng)庫(kù):
用來(lái)存儲(chǔ)配置文件的地方。
Config Server:
分布式配置中心,微服務(wù)中指定了連接倉(cāng)庫(kù)的位置以及賬號(hào)密碼等信息。
本地 GIT 倉(cāng)庫(kù):
在 Config Server 文件系統(tǒng)中,客戶單每次請(qǐng)求獲取配置信息時(shí),Config Server 從 GIT 倉(cāng)庫(kù)獲取最新配置到本地,然后在本地 GIT 倉(cāng)庫(kù)讀取并返回。當(dāng)遠(yuǎn)程倉(cāng)庫(kù)無(wú)法獲取時(shí),直接將本地倉(cāng)庫(kù)內(nèi)容返回。
ServerA/B:
具體的微服務(wù)應(yīng)用,他們指定了 Config Server 地址,從而實(shí)現(xiàn)外部化獲取應(yīng)用自己想要的配置信息。應(yīng)用啟動(dòng)時(shí)會(huì)向 Config Server 發(fā)起請(qǐng)求獲取配置信息進(jìn)行加載。
消息中心:
上述第二個(gè)架構(gòu)圖是基于消息總線的方式,依賴的外部的 MQ 組件,目前支持 Kafka、Rabbitmq。通過(guò) Config Server 配置中心提供的 /bus/refresh endpoint 作為生產(chǎn)者發(fā)送消息,客戶端接受到消息通過(guò)http接口形式從 Config Server 拉取配置。
服務(wù)注冊(cè)中心:
可以將 Config Server 注冊(cè)到服務(wù)注冊(cè)中心上比如 Eureka,然后客戶端通過(guò)服務(wù)注冊(cè)中心發(fā)現(xiàn)Config Server 服務(wù)列表,選擇其中一臺(tái) Config Server 來(lái)完成健康檢查以及獲取遠(yuǎn)端配置信息。
Spring Cloud Config 客戶端加載流程客戶端應(yīng)用從配置管理中獲取配置執(zhí)行流程:
1)應(yīng)用啟動(dòng)時(shí),根據(jù) bootstrap.yml 中配置的應(yīng)用名 {application}、環(huán)境名 {profile}、分支名 {label},向 Config Server 請(qǐng)求獲取配置信息。
2)Config Server 根據(jù)自己維護(hù)的 GIT 倉(cāng)庫(kù)信息與客戶端傳過(guò)來(lái)的配置定位去查找配置信息。
3)通過(guò) git clone 命令將找到的配置下載到 Config Server 的文件系統(tǒng)(本地GIT倉(cāng)庫(kù))
4)Config Server 創(chuàng)建 Spring 的 ApplicationContext 實(shí)例,并從 GIT 本地倉(cāng)庫(kù)中加載配置文件,最后讀取這些配置內(nèi)容返回給客戶端應(yīng)用。
5)客戶端應(yīng)用在獲取外部配置內(nèi)容后加載到客戶端的 ApplicationContext 實(shí)例,該配置內(nèi)容優(yōu)先級(jí)高于客戶端 Jar 包內(nèi)部的配置內(nèi)容,所以在 Jar 包中重復(fù)的內(nèi)容將不再被加載。
Spring Cloud Config 基于消息總線配置Config Server 作為配置中心 pom.xml 引入:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-kafka</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>Config Server 配置文件(yml格式):
server:port: ${CONFIG_SERVER_PORT:8021} spring:application:name: letv-mas-config# 配置了該項(xiàng),訪問(wèn)/bus/refresh?destination=應(yīng)用名:spring.application.index,如果未指定spring.application.index默認(rèn)使用「應(yīng)用名:server.port」index: ${CONFIG_SERVER_IP:127.0.0.1}cloud:config:server:git:# 基于 http 協(xié)議的單倉(cāng)庫(kù),每一個(gè)應(yīng)用創(chuàng)建一個(gè)目錄,每個(gè)目錄下創(chuàng)建配置文件uri: ${GIT_URI:http://xxxx/config.git}search-paths: '{application}'# 配置的 Git 倉(cāng)庫(kù)基于 http 協(xié)議的,必須配置用戶名和密碼username: ${GIT_USERNAME:config_server}password: ${GIT_PASSWORD:config@123}# 本地倉(cāng)庫(kù)目錄設(shè)定basedir: /letv/app/mas/config/repos# 本地倉(cāng)庫(kù)如果有臟數(shù)據(jù),則會(huì)強(qiáng)制拉取(默認(rèn)是false)force-pull: true# 配置中心啟動(dòng)后從 GIT 倉(cāng)庫(kù)下載,如果uri配置中使用了 {application} 作為倉(cāng)庫(kù)名,這里要使用默認(rèn)值false,否則啟動(dòng)報(bào)錯(cuò).clone-on-start: falsemanagement:security:enabled: false# 用戶認(rèn)證,客戶端應(yīng)用接入時(shí)加入安全認(rèn)證配置 security:user:name: configpassword: config2018basic:enabled: true # 基于消息總線的 MQ 配置 spring:cloud:stream:kafka:binder:zk-nodes: ${ZK_NODES:localhost:2181}brokers: ${KAFKA_BROKERS:localhost:9092}requiredAcks: -1configuration:security:protocol: SASL_PLAINTEXTsasl:mechanism: PLAINjaas:loginModule: org.apache.kafka.common.security.plain.PlainLoginModuleoptions:username: testpassword: test-secret# 開(kāi)啟跟蹤事件消息(默認(rèn)是false)bus:trace:enabled: true# 自定義 topic 主題destination: test.springcloud.configConfig Client 作為客戶端 pom.xml 引入:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-kafka</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>Config Client 配置文件(yml格式):
spring:application:name: letv-mas-clientindex: ${CLIENT_SERVER_IP:127.0.0.1}:${server.port}profiles:active: ${CLIENT_PROFILE:default}#include: busdev,streamdevcloud:config:uri: ${CONFIG_SERVER_DOMAIN:http://config.xxx.cn/}failFast: true #the client will halt with an Exceptionenabled: true# boostrap.yml 配置優(yōu)先于啟動(dòng)參數(shù)變量 spring.profiles.activeprofile: ${spring.profiles.active:${CLIENT_PROFILE:default}}label: master# 訪問(wèn)配置中心,用戶安全認(rèn)證username: configpassword: config2018# 激活定時(shí)任務(wù),當(dāng) GIT 版本發(fā)生變更后加載最新配置上下文watcher:enabled: true security:user:name: configpassword: config2018# 基于消息總線的 MQ 配置( Kakfa 隊(duì)列),如果zipkin中也使用 Kafka 隊(duì)列,那么需要通過(guò)binder 形式配置做隔離,否則會(huì)互相影響,無(wú)法下發(fā)配置消息。 spring:cloud:stream:# 自定義開(kāi)關(guān)enabled: true# 指定中間件default-binder: config-kafkabinders:config-kafka:type: kafkaenvironment:spring:cloud:stream:kafka:binder:zkNodes: ${ZK_NODES:localhost:2181}brokers: ${KAFKA_BROKERS:localhost:9092}# 生產(chǎn)者確認(rèn),0、1、-1,默認(rèn)為1。0為不確認(rèn),1為leader單確認(rèn),-1為同步副本確認(rèn)。-1的情況下消息可靠性更高。required-acks: -1# 是否自動(dòng)創(chuàng)建topic,默認(rèn)為true。設(shè)為false的情況下,依賴手動(dòng)配置broker相關(guān)topic>配置,如果topic不存在binder則無(wú)法啟動(dòng)。auto-create-topics: trueconfiguration:security:protocol: SASL_PLAINTEXTsasl:mechanism: PLAINjaas:loginModule: org.apache.kafka.common.security.plain.PlainLoginModuleoptions:username: testpassword: test-secretbus:# 是否啟用busenabled: true# Bus 使用的隊(duì)列或 Topic,Kafka 中的 topic,Rabbitmq 中的 queuedestination: test.springcloud.configtrace:# 是否啟用 Bus 事件跟蹤,可以通過(guò) /trace 頁(yè)面查看enabled: truerefresh:# 是否發(fā)送 refresh 事件,開(kāi)啟時(shí)支持基于 config 文件變更的動(dòng)態(tài)配置enabled: trueenv:# 是否開(kāi)啟 env 事件,開(kāi)啟時(shí)支持直接動(dòng)態(tài)配置相應(yīng)環(huán)境變量,如 /bus/env?arg1=value1&arg2=value2enabled: true Spring Cloud Config 中的占位符占位符的使用:
這里的 {application} 代表了應(yīng)用名,當(dāng)客戶端向 Config Server 發(fā)起獲取配置請(qǐng)求時(shí),Config Server 會(huì)根據(jù)客戶端的 spring.application.name 信息來(lái)填充 {application} 占位符以定位配置資源的存儲(chǔ)位置。
注意:{label} 參數(shù)很特別,如果 GIT 分支和標(biāo)簽包含 “/”,那么 {label} 參數(shù)在 HTTP 的 URL 中應(yīng)用使用 “(_)” 替代,以避免改變了 URI 含義,指向到其他 URI 資源。
為什么要有占位符?
當(dāng)使用 GIT 作為配置中心來(lái)存儲(chǔ)各個(gè)微服務(wù)應(yīng)用的配置文件時(shí),URI 中的占位符的使用可以幫助我們規(guī)劃和實(shí)現(xiàn)通用的倉(cāng)庫(kù)配置。
Spring Cloud Config 倉(cāng)庫(kù)最佳實(shí)踐本地倉(cāng)庫(kù):?
Spring Cloud 的 D、E 版本中默認(rèn)存儲(chǔ)到 /var/folders/ml/9rww8x69519fwqlwlt5jrx700000gq/T/config-repo-2486127823875015066目錄下。
在 B 版本中,未實(shí)際測(cè)試過(guò),存儲(chǔ)到臨時(shí)目錄 /tmp/config-repo-隨機(jī)數(shù)目錄下。 為了避免一些不可預(yù)知的問(wèn)題,我們?cè)O(shè)置一個(gè)固定的本地GIT倉(cāng)庫(kù)目錄。
spring.cloud.config.server.git.basedir=${user.home}/local-config-repo?這個(gè)配置中,如果${user.home} 目錄下發(fā)現(xiàn) local-config-repo 不存在,在 Config Server 啟動(dòng)后會(huì)自動(dòng)創(chuàng)建,并從 GIT 遠(yuǎn)程倉(cāng)庫(kù)下載配置存儲(chǔ)到這個(gè)位置。
遠(yuǎn)程倉(cāng)庫(kù)實(shí)踐:?
單倉(cāng)庫(kù)目錄:每一個(gè)項(xiàng)目對(duì)應(yīng)一個(gè)倉(cāng)庫(kù)?spring.cloud.config.server.git.uri=https://gitee.com/ldwds/{application}
多倉(cāng)庫(kù)目錄:同一個(gè)倉(cāng)庫(kù)下,每個(gè)項(xiàng)目一個(gè)目錄?spring.cloud.config.server.git.uri=https://gitee.com/ldwds/config-repo-demo.git?spring.cloud.config.server.git.search-paths='{application}'
1)單倉(cāng)庫(kù)目錄注意事項(xiàng):
spring.cloud.config.server.git.uri=[https://gitee.com/ldwds/config-repo-demo/](https://gitee.com/ldwds/config-repo-demo/) spring.cloud.config.serversearch-paths:’{application}'客戶端應(yīng)用啟動(dòng)前,在 config-repo-demo 倉(cāng)庫(kù)下創(chuàng)建子目錄,子目錄名稱就是配置中指定的spring.application.name 應(yīng)用名。?
否則,工程中引用的屬性找不到,會(huì)報(bào)如下錯(cuò)誤:?Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'from' in value "${from}"
2)多倉(cāng)庫(kù)目錄注意事項(xiàng):?
這種方式不能設(shè)置參數(shù)?spring.cloud.config.server.git.force-pull=true?和?spring.cloud.config.server.git.clone-on-start=true?
否則啟動(dòng)會(huì)報(bào)錯(cuò),也很好理解因?yàn)槭褂昧?{applicatoin} 作為占位符,沒(méi)有指明具體的倉(cāng)庫(kù)名,所以無(wú)法強(qiáng)制拉取遠(yuǎn)程倉(cāng)庫(kù)配置。
如果你設(shè)置了本地倉(cāng)庫(kù)目錄比如?spring.cloud.config.server.git.basedir=/data/config-repos/local-config-repo?Config Server 啟動(dòng)后會(huì)自動(dòng)創(chuàng)建 /data/config-repos 目錄,并創(chuàng)建 config-repo-隨機(jī)數(shù)命名的倉(cāng)庫(kù)名錄,這個(gè)倉(cāng)庫(kù)下的內(nèi)容來(lái)自于健康檢查的默認(rèn)倉(cāng)庫(kù)app。
客戶端應(yīng)用啟動(dòng)后,會(huì)根據(jù) {application} 應(yīng)用名去查找該倉(cāng)庫(kù),Config Server 從匹配 Git 倉(cāng)庫(kù)并 clone 到 config-repo-隨機(jī)數(shù)的目錄下。
如果 Config Server 重啟了,客戶端應(yīng)用通過(guò) /bus/refresh 刷新配置,因?yàn)椴](méi)有緩存之前的倉(cāng)庫(kù)名,所以會(huì)自動(dòng)創(chuàng)建一個(gè) config-repo-隨機(jī)數(shù) 的倉(cāng)庫(kù)目錄并從 Git clone 數(shù)據(jù)。?
如果 Config Server 已有本地倉(cāng)庫(kù),客戶端重啟或/bus/refresh刷新配置則 Config Server 不會(huì)重建新的倉(cāng)庫(kù)。
配置中心本地倉(cāng)庫(kù)執(zhí)行原理:?
本地倉(cāng)庫(kù)是否存在根據(jù) basedir 目錄下是否包含.git 隱藏文件。
如果本地倉(cāng)庫(kù)不存在,則從遠(yuǎn)端倉(cāng)庫(kù) clone 數(shù)據(jù)到本地;如果本地倉(cāng)庫(kù)存在,則從遠(yuǎn)程倉(cāng)庫(kù) fetch 最新數(shù)據(jù)到本地。
然后 checkout 到指定 label,從遠(yuǎn)端倉(cāng)庫(kù) merge 數(shù)據(jù),并獲取當(dāng)前 label 分支最新的HEAD 版本,以及默認(rèn)的應(yīng)用名 app 作為環(huán)境信息返回。
Spring Cloud Config健康檢查問(wèn)題剖析健康檢查 pom.xml 中引入:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId> </dependency>添加上述依賴后,默認(rèn)開(kāi)啟健康檢查。如果不需要健康檢查,可以通過(guò)?spring.cloud.config.server.health.enabled=false?參數(shù)設(shè)定關(guān)閉。
如果配置為:?spring.cloud.config.server.git.uri=[https://gitee.com/ldwds/config-repo-demo.git](https://gitee.com/ldwds/config-repo-demo.git)?默認(rèn)開(kāi)啟了健康檢查,我開(kāi)始認(rèn)為默認(rèn)檢查應(yīng)用名稱為 app,profiles 為 default,label 為 null進(jìn)行監(jiān)控(源碼中看到的)。
但是 GIT 配置倉(cāng)庫(kù)下并沒(méi)有 app 應(yīng)用,此時(shí)訪問(wèn) /health,監(jiān)控狀態(tài)仍然是 UP?
{"status": "UP" }上述理解是錯(cuò)誤的,原因分析如下:
這個(gè)主要跟配置中心指定的 GIT 倉(cāng)庫(kù)地址有關(guān)系。
如果倉(cāng)庫(kù)地址指定的是?https://gitee.com/ldwds/{application} ,檢查監(jiān)視器會(huì)將 {application} 替換為默認(rèn)應(yīng)用名 app 作為倉(cāng)庫(kù)地址,此時(shí)會(huì)在 {user.home} 目錄下創(chuàng)建 config-repo-隨機(jī)數(shù)作為 {application} 應(yīng)用的本地倉(cāng)庫(kù)(如:/Users/liudewei1228/config-repo-7949870192520306956)。
即使設(shè)置了spring.config.server.git.basedir=${user.home}/local-config-repo/也不會(huì)被使用到。 為什么?因?yàn)?{application} 作為倉(cāng)庫(kù),是個(gè)動(dòng)態(tài)的,可能會(huì)有多個(gè) {application} 項(xiàng)目倉(cāng)庫(kù),所以不會(huì)使用 basedir 特定目錄作為本地倉(cāng)庫(kù)。
如下參數(shù)設(shè)置健康檢查的配置:
spring.cloud.config.server.health.repositories.config-repo-demo.name=應(yīng)用名 spring.cloud.config.server.health.repositories.config-repo-demo.label=分支 spring.cloud.config.server.health.repositories.config-repo-demo.profiles=環(huán)境變量找到環(huán)境信息即顯示狀態(tài) UP,此過(guò)程出現(xiàn)任何異常(如找不到倉(cāng)庫(kù) NoSuchRespositoryException)就會(huì)顯示 DOWN 狀態(tài)。
在uri中包含 {application} 作為倉(cāng)庫(kù)情況下,客戶端應(yīng)用在啟用前需提前創(chuàng)建好spring.application.name=config-client應(yīng)用名作為倉(cāng)庫(kù),否則會(huì)導(dǎo)致無(wú)法啟用。(因?yàn)?{application} 被認(rèn)為是一個(gè)項(xiàng)目倉(cāng)庫(kù),并不是一個(gè)目錄)。
源碼詳見(jiàn):ConfigServerHealthIndicator.java 的 doHealthCheck 方法。?
配置正確倉(cāng)庫(kù)的?name、label、profiles,訪問(wèn) /health 接口顯示 sources,這個(gè) sources 中的地址無(wú)法訪問(wèn)的,實(shí)際只是一個(gè)標(biāo)識(shí)的作用。
訪問(wèn)/health結(jié)果:
{"status": "UP","repositories": [{"sources": ["https://gitee.com/ldwds/config-repo-demo/config-client/config-client.properties";],"name": "config-client","profiles": ["default"],"label": "master"}] }否則,找不到指定倉(cāng)庫(kù)的信息,只會(huì)顯示如下信息:
{"status": "UP","repositories": [{"name": "config-client","profiles": ["default"],"label": "master"}] }最新 Spring Cloud Config 改進(jìn)了很多問(wèn)題,大家可以結(jié)合官網(wǎng)進(jìn)一步學(xué)習(xí)了解。
本文對(duì) Spring Cloud Config (Spring Cloud E 版本)的基本概念、基于消息總線的配置使用、倉(cāng)庫(kù)目錄實(shí)踐、健康檢查的實(shí)踐以及實(shí)踐中遇到的問(wèn)題進(jìn)行了剖析,希望有使用到這個(gè)配置中心的朋友們有所幫助。
目前微服務(wù)架構(gòu)中選型時(shí),推薦使用國(guó)內(nèi)開(kāi)源的配置中心:Apollo配置中心(攜程開(kāi)源)、Nacos注冊(cè)&配置中心(阿里巴巴開(kāi)源)。
有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud Config 配置中心实践过程中,你需要了解这些细节!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 成就架构师,到底是怎样的一种体验?
- 下一篇: NYOJ 585 取石子(六)