歡迎關注個人公衆號,掃碼關注,解鎖更多精彩文章,與你一同成長~java
本文主要介紹 Spring Cloud Config 基本概念、實踐過的配置及遇到的問題進行剖析。關於如何啓動運行配置中心能夠參考官方 Demo。node
本文使用的Spring Cloud 版本:Edgware.SR3git
Spring Cloud Config 用來爲分佈式系統中的基礎設施和微服務應用提供集中化的外部配置支持。spring
SCC做用:apache
實現了對服務端和客戶端中環境變量和屬性配置的抽象映射。
SCC優點:bootstrap
默認採用 GIT 存儲配置信息,自然支持對配置信息的版本管理。
Spring Cloud Config 架構圖:緩存
基於消息總線的架構圖:安全
如上圖所示,架構圖中的幾個主要元素做用:
遠程 GIT 倉庫:架構
用來存儲配置文件的地方。
Config Server:app
分佈式配置中心,微服務中指定了鏈接倉庫的位置以及帳號密碼等信息。
本地 GIT 倉庫:
在 Config Server 文件系統中,客戶單每次請求獲取配置信息時,Config Server 從 GIT 倉庫獲取最新配置到本地,而後在本地 GIT 倉庫讀取並返回。當遠程倉庫沒法獲取時,直接將本地倉庫內容返回。
ServerA/B:
具體的微服務應用,他們指定了 Config Server 地址,從而實現外部化獲取應用本身想要的配置信息。應用啓動時會向 Config Server 發起請求獲取配置信息進行加載。
消息中心:
上述第二個架構圖是基於消息總線的方式,依賴的外部的 MQ 組件,目前支持 Kafka、Rabbitmq。經過 Config Server 配置中心提供的 /bus/refresh endpoint 做爲生產者發送消息,客戶端接受到消息經過http接口形式從 Config Server 拉取配置。
服務註冊中心:
能夠將 Config Server 註冊到服務註冊中心上好比 Eureka,而後客戶端經過服務註冊中心發現Config Server 服務列表,選擇其中一臺 Config Server 來完成健康檢查以及獲取遠端配置信息。
客戶端應用從配置管理中獲取配置執行流程:
1)應用啓動時,根據 bootstrap.yml 中配置的應用名 {application}、環境名 {profile}、分支名 {label},向 Config Server 請求獲取配置信息。
2)Config Server 根據本身維護的 GIT 倉庫信息與客戶端傳過來的配置定位去查找配置信息。
3)經過 git clone 命令將找到的配置下載到 Config Server 的文件系統(本地GIT倉庫)
4)Config Server 建立 Spring 的 ApplicationContext 實例,並從 GIT 本地倉庫中加載配置文件,最後讀取這些配置內容返回給客戶端應用。
5)客戶端應用在獲取外部配置內容後加載到客戶端的 ApplicationContext 實例,該配置內容優先級高於客戶端 Jar 包內部的配置內容,因此在 Jar 包中重複的內容將再也不被加載。
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 # 配置了該項,訪問/bus/refresh?destination=應用名:spring.application.index,若是未指定spring.application.index默認使用「應用名:server.port」 index: ${CONFIG_SERVER_IP:127.0.0.1} cloud: config: server: git: # 基於 http 協議的單倉庫,每個應用建立一個目錄,每一個目錄下建立配置文件 uri: ${GIT_URI:http://xxxx/config.git} search-paths: '{application}' # 配置的 Git 倉庫基於 http 協議的,必須配置用戶名和密碼 username: ${GIT_USERNAME:config_server} password: ${GIT_PASSWORD:config@123} # 本地倉庫目錄設定 basedir: /letv/app/mas/config/repos # 本地倉庫若是有髒數據,則會強制拉取(默認是false) force-pull: true # 配置中心啓動後從 GIT 倉庫下載,若是uri配置中使用了 {application} 做爲倉庫名,這裏要使用默認值false,不然啓動報錯. clone-on-start: false management: security: enabled: false # 用戶認證,客戶端應用接入時加入安全認證配置 security: user: name: config password: config2018 basic: enabled: true # 基於消息總線的 MQ 配置 spring: cloud: stream: kafka: binder: zk-nodes: ${ZK_NODES:localhost:2181} brokers: ${KAFKA_BROKERS:localhost:9092} requiredAcks: -1 configuration: security: protocol: SASL_PLAINTEXT sasl: mechanism: PLAIN jaas: loginModule: org.apache.kafka.common.security.plain.PlainLoginModule options: username: test password: test-secret # 開啓跟蹤事件消息(默認是false) bus: trace: enabled: true # 自定義 topic 主題 destination: test.springcloud.config
Config 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-client index: ${CLIENT_SERVER_IP:127.0.0.1}:${server.port} profiles: active: ${CLIENT_PROFILE:default} #include: busdev,streamdev cloud: config: uri: ${CONFIG_SERVER_DOMAIN:http://config.xxx.cn/} failFast: true #the client will halt with an Exception enabled: true # boostrap.yml 配置優先於啓動參數變量 spring.profiles.active profile: ${spring.profiles.active:${CLIENT_PROFILE:default}} label: master # 訪問配置中心,用戶安全認證 username: config password: config2018 # 激活定時任務,當 GIT 版本發生變動後加載最新配置上下文 watcher: enabled: true security: user: name: config password: config2018 # 基於消息總線的 MQ 配置( Kakfa 隊列),若是zipkin中也使用 Kafka 隊列,那麼須要經過binder 形式配置作隔離,不然會互相影響,沒法下發配置消息。 spring: cloud: stream: # 自定義開關 enabled: true # 指定中間件 default-binder: config-kafka binders: config-kafka: type: kafka environment: spring: cloud: stream: kafka: binder: zkNodes: ${ZK_NODES:localhost:2181} brokers: ${KAFKA_BROKERS:localhost:9092} # 生產者確認,0、一、-1,默認爲1。0爲不確認,1爲leader單確認,-1爲同步副本確認。-1的狀況下消息可靠性更高。 required-acks: -1 # 是否自動建立topic,默認爲true。設爲false的狀況下,依賴手動配置broker相關topic>配置,若是topic不存在binder則沒法啓動。 auto-create-topics: true configuration: security: protocol: SASL_PLAINTEXT sasl: mechanism: PLAIN jaas: loginModule: org.apache.kafka.common.security.plain.PlainLoginModule options: username: test password: test-secret bus: # 是否啓用bus enabled: true # Bus 使用的隊列或 Topic,Kafka 中的 topic,Rabbitmq 中的 queue destination: test.springcloud.config trace: # 是否啓用 Bus 事件跟蹤,能夠經過 /trace 頁面查看 enabled: true refresh: # 是否發送 refresh 事件,開啓時支持基於 config 文件變動的動態配置 enabled: true env: # 是否開啓 env 事件,開啓時支持直接動態配置相應環境變量,如 /bus/env?arg1=value1&arg2=value2 enabled: true
佔位符的使用:
這裏的 {application} 表明了應用名,當客戶端向 Config Server 發起獲取配置請求時,Config Server 會根據客戶端的 spring.application.name 信息來填充 {application} 佔位符以定位配置資源的存儲位置。
注意:{label} 參數很特別,若是 GIT 分支和標籤包含 「/」,那麼 {label} 參數在 HTTP 的 URL 中應用使用 「(_)」 替代,以免改變了 URI 含義,指向到其餘 URI 資源。
爲何要有佔位符?
當使用 GIT 做爲配置中心來存儲各個微服務應用的配置文件時,URI 中的佔位符的使用能夠幫助咱們規劃和實現通用的倉庫配置。
本地倉庫:
Spring Cloud 的 D、E 版本中默認存儲到 /var/folders/ml/9rww8x69519fwqlwlt5jrx700000gq/T/config-repo-2486127823875015066目錄下。
在 B 版本中,未實際測試過,存儲到臨時目錄 /tmp/config-repo-隨機數目錄下。
爲了不一些不可預知的問題,咱們設置一個固定的本地GIT倉庫目錄。
經過 spring.cloud.config.server.git.basedir=${user.home}/local-config-repo
若是${user.home}目錄下發現local-config-repo不存在,在Config Server啓動後會自動建立,並從GIT遠程倉庫下載配置存儲到這個位置。
遠程倉庫實踐:
單倉庫目錄:
每個項目對應一個倉庫spring.cloud.config.server.git.uri=https://gitee.com/ldwds/{application}
多倉庫目錄:
同一個倉庫下,每一個項目一個目錄spring.cloud.config.server.git.uri=https://gitee.com/ldwds/config-repo-demo.git
spring.cloud.config.server.git.search-paths='{application}'
1)單倉庫目錄注意事項:
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}'
客戶端應用啓動前,在 config-repo-demo 倉庫下建立子目錄,子目錄名稱就是配置中指定的spring.application.name 應用名。
不然,工程中引用的屬性找不到,會報以下錯誤:Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'from' in value "${from}"
2)多倉庫目錄注意事項:
這種方式不能設置參數 spring.cloud.config.server.git.force-pull=true
和 spring.cloud.config.server.git.clone-on-start=true
不然啓動會報錯,也很好理解由於使用了 {applicatoin} 做爲佔位符,沒有指明具體的倉庫名,因此沒法強制拉取遠程倉庫配置。
若是你設置了本地倉庫目錄好比 spring.cloud.config.server.git.basedir=/data/config-repos/local-config-repo
Config Server 啓動後會自動建立 /data/config-repos 目錄,並建立 config-repo-隨機數命名的倉庫名錄,這個倉庫下的內容來自於健康檢查的默認倉庫app。
客戶端應用啓動後,會根據 {application} 應用名去查找該倉庫,Config Server 從匹配 Git 倉庫並 clone 到 config-repo-隨機數的目錄下。
若是 Config Server 重啓了,客戶端應用經過 /bus/refresh 刷新配置,由於並無緩存以前的倉庫名,因此會自動建立一個 config-repo-隨機數 的倉庫目錄並從 Git clone 數據。
若是 Config Server 已有本地倉庫,客戶端重啓或 /bus/refresh 刷新配置則 Config Server 不會重建新的倉庫。
配置中心本地倉庫執行原理:
本地倉庫是否存在根據 basedir 目錄下是否包含.git 隱藏文件。若是本地倉庫不存在,則從遠端倉庫 clone 數據到本地;若是本地倉庫存在,則從遠程倉庫 fetch 最新數據到本地;而後 checkout 到指定 label,從遠端倉庫 merge 數據,並獲取當前 label 分支最新的HEAD 版本,以及默認的應用名 app 做爲環境信息返回。
健康檢查 pom.xml 中引入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency>
添加上述依賴後,默認開啓健康檢查。若是不須要健康檢查,能夠經過 spring.cloud.config.server.health.enabled=false
參數設定關閉。
若是配置爲: spring.cloud.config.server.git.uri=[https://gitee.com/ldwds/config-repo-demo.git](https://gitee.com/ldwds/config-repo-demo.git)
默認開啓了健康檢查,我開始認爲默認檢查應用名稱爲 app,profiles 爲 default,label 爲 null進行監控(源碼中看到的)。
可是 GIT 配置倉庫下並無 app 應用,此時訪問 /health,監控狀態仍然是 UP?
{ "status": "UP" }
上述理解是錯誤的,緣由分析以下:
這個主要跟配置中心指定的 GIT 倉庫地址有關係,若是倉庫地址指定的是 https://gitee.com/ldwds/{application} ,檢查監視器會將 {application} 替換爲默認應用名 app 做爲倉庫地址,此時會在 {user.home} 目錄下建立 config-repo-隨機數做爲 {application} 應用的本地倉庫(如:/Users/liudewei1228/config-repo-7949870192520306956)。即便設置了
spring.config.server.git.basedir=${user.home}/local-config-repo/
也不會被使用到。
爲何?由於 {application} 做爲倉庫,是個動態的,可能會有多個 {application} 項目倉庫,因此不會使用 basedir 特定目錄做爲本地倉庫。
以下參數設置健康檢查的配置:
spring.cloud.config.server.health.repositories.config-repo-demo.name=應用名 spring.cloud.config.server.health.repositories.config-repo-demo.label=分支 spring.cloud.config.server.health.repositories.config-repo-demo.profiles=環境變量
找到環境信息即顯示狀態 UP,此過程出現任何異常(如找不到倉庫 NoSuchRespositoryException)就會顯示 DOWN 狀態。
在uri中包含 {application} 做爲倉庫狀況下,客戶端應用在啓用前需提早建立好spring.application.name=config-client應用名做爲倉庫,不然會致使沒法啓用。(由於 {application} 被認爲是一個項目倉庫,並非一個目錄)。
源碼詳見:ConfigServerHealthIndicator.java 的 doHealthCheck 方法。
配置正確倉庫的 name、label、profiles,訪問 /health 接口顯示 sources,這個 sources 中的地址沒法訪問的,實際只是一個標識的做用。
訪問/health結果:
{ "status": "UP", "repositories": [ { "sources": [ "https://gitee.com/ldwds/config-repo-demo/config-client/config-client.properties"; ], "name": "config-client", "profiles": [ "default" ], "label": "master" } ] }
不然,找不到指定倉庫的信息,只會顯示以下信息:
{ "status": "UP", "repositories": [ { "name": "config-client", "profiles": [ "default" ], "label": "master" } ] }
最新 Spring Cloud Config 改進了不少問題,你們能夠結合官網進一步學習瞭解。
本文對 Spring Cloud Config (Spring Cloud E 版本)的基本概念、基於消息總線的配置使用、倉庫目錄實踐、健康檢查的實踐以及實踐中遇到的問題進行了剖析,但願有使用到這個配置中心的朋友們有所幫助。
目前微服務架構中選型時,推薦使用國內開源的配置中心:Apollo配置中心(攜程開源)、Nacos註冊&配置中心(阿里巴巴開源)。
歡迎關注個人公衆號,掃碼關注,解鎖更多精彩文章,與你一同成長~