????Spring Cloud Config是Spring Cloud團隊創建的一個全新項目,用來為分布式系統中的基礎設施和微服務應用提供集中化的外部配置支持的,它分為服務端與客戶端兩個部分。其中服務端也成為分布式配置中心,它是一個獨立的微服務應用,用來連接配置倉庫并為客戶端提供獲取配置信息、加密/解密信息等訪問接口;而客戶端則是微服務架構中的各個微服務應用或基礎設施,它們通過指定的配置中心來管理應用資源與業務相關的配置內容,并在啟動的時候從配置中心獲取和加載配置信息。Spring Cloud Config實現了對服務端和客戶端中環境變量和屬性配置的抽象映射,所以它除了適用于Spring構建的應用程序之外,也可以在任何其他語言運行的應用程序中使用。由于Spring Cloud Config實現的配置中心默認采用Git來存儲配置信息,所以使用spring Cloud Config構建的配置服務器,天然就支持對微服務應用配置信息的版本管理,并且可以通過Git等客戶端工具來方便地管理和訪問配置內容。當然它也提供了對其他存儲方式的支持,比如SVN倉庫、本地化文件系統。接下來,我們從一個簡單的入門示例開始學習Spring Cloud Config服務端以及客戶端的詳細構建與使用方法。
????在本節中,我們將演示如何構建基于Git存儲的分布式配置中心,同時對配置的詳細規則進行講解,并在客戶端中演示如何通過配置指定微服務應用的所屬配置中心,并讓其能夠從配置中心獲取配置信息并綁定到代碼中的整個過程。
通過Spring Cloud Config構建一個分布式配置中心非常簡單,只需要以下三步。
??????創建一個基礎的Spring Boot工程,命名為config-server,并在pom.xml中引入下面的依賴:
??????創建Spring Boot的程序主類,并添加@EnableConfigServer注解,開啟Spring Cloud Config的服務端功能。
??????在application.properties中添加配置服務的基本信息以及Git倉庫的相關信息,如下所示:
其中Git等配置信息分別表示如下內容:
??????spring.cloud.config.server.git.uri:配置Git倉庫位置
??????spring.cloud.config.server.git.searchPaths:配置倉庫路徑下單相對搜索位置,可以配置多個
??????spring.cloud.config.server.git.username:訪問Git倉庫的用戶名
??????spring.cloud.config.server.git.password:訪問Git倉庫的用戶密碼
??????spring.cloud.config.label:git倉庫分支名稱
????到這里,使用一個通過Spring Cloud Config實現,并使用Git管理內容等分布式配置中心就完成了。我們可以將該應用先啟動起來,確保沒有錯誤產生,然后進入下面等地學習內容。
????為了驗證上面完全的分布式配置中心config-server,根據git配置信息中指定的倉庫位置,在https://github.com/erghjmncq6643981/demo/tree/master下創建respo目錄作為配置倉庫,并根據不同環境新建下面4個配置文件:
??????didispace.properties
??????didispace-dev.properties
??????didispace-test.properties
??????didispace-prod.properties
????在這4個配置文件中均設置了一個from屬性,并為每個配置文件分別設置了不同的值,如下所示:
??????from=git-default-1.0
??????from=git-dev-1.0
??????from=git-test-1.0
??????from=git-prod-1.0
????為了測試版本控制,在該Git倉庫的master分支中,我們為from屬性加入1.0的后綴,同時創建一個config-label-test分支,并將各配置文件中的值用2.0作為后綴。
????完成了這些準備工作之后,我們就可以通過瀏覽器、POSTMAN或CURL等工具直接來訪問我們的配置內容了。訪問配置信息的URL與配置文件的映射關系如下所示:
??????/{application}/{profile}[/{label}]
??????/{application}-{profile}.yml
??????/{application}-{profile}.properties
??????/{label}/{application}-{profile}.properties
????上面的url會映射{application}-{profile}.properties對應的配置文件,其中{label}對應Git上不同的分支,默認為master。我們可以嘗試構造不同的url來訪問不同的配置內容,比如要訪問kyle分支,didispace應用的prod不同的配置內容,比如,要訪問kyle分支,didispace應用的prod環境,就可以訪問這個url:http://localhost:8888/didispace/prod/kyle,并獲得如下返回信息。
????我們可以看到該JSON中返回了應用名didispace,環境名prod,分支名kyle,以及默認default環境和prod環境的配置內容。另外,之前沒有提到過的version,從下圖我們可以觀察到,它對應的是在Git上的commit號。
????同時,我們可以看到config-server的控制臺中還輸出了下面的內容,配置服務器在從Git中獲取配置信息后,會存儲一份在config-server的文件系統中,實質上config-sever是通過git clone的文件系統中,實質上config-server是通過git clone命令將配置內容復制了一份在本地存儲,然后讀取這些內容并返回給微服務應用進行加載。
????config-server通過Git在本地倉庫暫存,可以有效防止當Git倉庫出現故障而引起無法加載配置信息的情況。我們可以通過斷開網絡,再次發起http://localhost:8888/didispace/prod/kyle請求,在控制臺中可輸出Could not pull remote for kyle,但是它依然會為該請求返回配置內容,這些內容源于之前訪問時存于config-server本地文件系統中的配置內容。
????在動手實踐了上面關于Spring Cloud Config的基礎入門內容之后,在這里我們深入理解一些它是如何運作起來的。下面所示的是上一節我們構建案例的基本結構。
????其中,主要包含下面幾個要素。
??????遠程Git倉庫:用來存儲配置文件的地方,上例中我們用來存儲針對應用名為didispace的多環境配置文件:didispace-{profile}.properties。
??????Config Server:這是我們上面構建的分布式配置中心,config-server工程,在該工程中指定了所要連接的Git倉庫位置以及賬戶、密碼等連接信息。
??????本地Git倉庫:在Config Server的文件系統中,每次客戶端請求獲取配置信息時,Config Server從Git倉庫中獲取最新配置到本地,然后客戶端請求獲取配置信息時,Config Server從Git倉庫中獲取最新配置到本地,然后在本地Git倉庫中讀取并返回。當遠程倉庫無法獲取時,直接將本地內容返回。
??????Service A、Service B:具體的微服務應用,它們指定了Config Server的地址,從而實現從外部獲取應用自己要用的配置信息。這些應用在啟動的時候,會向Config Server請求獲取配置信息來進行加載。
客戶端應用從配置管理中獲取配置信息遵從下面的執行流程:
????應用啟動時,根據bootstrap.properties中配置的應用名{application}、環境名{profile}、分支名{label},向Config Server請求獲取配置信息。
????Config Server根據自己維護的Git倉庫信息和客戶端傳遞過來的配置定位信息去查找配置信息。
????通過git clone命令將找到的配置信息下載到Config Server的文件系統中。
????Config Server創建Spring的Applicationcontext實例,并從Git本地倉庫中加載配置文件,最后將這些配置內容讀取出來返回給客戶端應用。
????客戶端應用在獲得外部配置文件后加載到客戶端的ApplicationContext實例,該配置內容的優先級高于客戶端Jar包內部的配置內容,所以在Jar包中重復的內容將不再被加載。
????Config Server巧妙地通過git clone將配置信息存于本地,起到緩存的作用,即使當Git服務端無法訪問的時候,依賴可以取Config Server中的緩存內容進行使用。
????在Spring Cloud Config的服務端,對于配置倉庫的默認實現采用了Git。Git非常適用于存儲配置內容,它可以非常方便地使用各種第三方工具來對其進行管理更新和版本化,同時Git倉庫倉庫的Hook功能還可以幫助我們實時監控配置內容的修改。其中,Git自身的版本控制功能正是其他一些配置中心所欠缺的,通過Git進行存儲意味著,一個應用的不同部署實例可以從Spring Cloud Config的服務端獲取不同的版本配置,從而支持一些特殊的應用場景。
????由于Spring Cloud Config中默認使用Git,所以對于Git的配置非常簡單,只需在Config Server的application.properties中設置spring.cloud.config.server.git.uri屬性,為其指定Git倉庫的網絡地址和賬戶信息即可,比如在快速入門一節中的例子:
????如果我們將該值通過file://前綴來設置為一個文件地址(在Windows系統中,需要使用file:///來定位文件內容),那么它將以本地倉庫的方式運行,這樣我們就可以脫離Git服務端來進行調試與開發,比如:
????其中,${user.home}代表當前用戶的所屬目錄。file://配置的本地文件系統方式雖然對于本地開發調試時使用非常方便,但是該方式也僅用于開發與測試,在生產環境中,請務必大家自己的Git倉庫來存儲配置資源。
????{application}、{profile}、{label}這些占位符除了用于標識配置文件的規則之外,還可以用于Config Server中對Git倉庫地址的URI配置。比如,我們可以通過{application}占位符來實現一個應用對應一個Git倉庫目錄的配置效果,具體配置實現如下:
????其中,{application}代表了應用名,所以當客戶端應用向Config Server發起獲取配置的請求時,Config Server會根據客戶端spring.application.name信息來填充{application}占位符以定位配置資源的存儲位置,從而實現根據微服務應用的屬性動態獲取不同的位置的配置。另外,這些占位符中,{label}參數較為特別,如果Git的分支和標簽名包含"/",那么{label}參數在HTTP的URL中應該使用"(_)"來替代,以避免改變了URI含義,指向到其他的URI資源。
github中目錄結構必須如下所示
注意:假如使用yml文件,需要在占位符上加'',如下圖所示
?當我們使用Git作為配置中心來存儲各個微服務應用配置文件的時候,該功能會變得非常有用,通過在URI中使用占位符可以幫助我們規劃和實現通用的倉庫配置。例如,我們可以對微服務應用做如下規劃。
??????代碼庫:使用服務名作為Git倉庫名稱,比如會員服務的代碼庫https://github.com/erghjmncq6643981/demo/respo/member-service。
??????配置庫:使用服務名加上-config后綴作為Git倉庫名稱,比如上面會員服務對應的配置庫地址位置https://github.com/erghjmncq6643981/demo/respo/member-service-config。
????這時,我們就可以通過使用spring.cloud.config.server.git.rui=https://github.com/erghjmncq6643981/demo/respo/{application}-config配置,來同時匹配多個不同服務的配置倉庫。
Config Server除了可以通過application和profile模式來匹配配置倉庫之外,還支持通過帶有通配符的表達式來匹配,以實現更為復雜的配置需求。并且當我們有多個匹配規則的時候,還可以用逗號來分割多個{application}/{profile}配置規則,比如:
????上述配置內容通過spring.cloud.config.server.git.uri屬性,指定了一個默認的倉庫位置,當使用{application}/{profile}模式未能匹配到合適的倉庫時,就將在該默認倉庫位置下獲取配置信息。除此之外,還配置了三個倉庫,分別是dev、test、prod。其中dev倉庫匹配dev/*的模式,所以無論profile是什么,它都能匹配application名稱為dev的應用。并且我們可以注意到,它存儲的配置文件位置還采用了Config Server的本地文件系統中的內容。對于此配置,我們可以通過訪問http://localhost8888/dev/profile的請求來驗證到該倉庫的配置內容,其中profile可以為任意值。而test和prod倉庫均使用了Git倉庫的存儲,并且test倉庫未配置匹配規則,所以它只匹配application名為test的應用;prod倉庫則需要匹配application為prod并且profile為pp開頭,或者application為online并且profile為oo開頭的應用和環境。
????當配置多個倉庫的時候,Config Server在啟動時會直接克隆第一個倉庫的配置庫,其他的配置庫只有在請求時才會克隆到本地,所以對于倉庫排列可以根據配置內容的重要程度有所區分。另外,如果表達式是以通配符開始的,那么需要使用引號將配置內容引起來。
????由于配置中心存儲的內容比較敏感,做一定的安全處理是必需的。為配置中心實現安全保護的方式有很多,比如物理網絡限制、OAuth2授權等。不過,由于我們的微服務應用和配置中心都構建于Spring Boot基礎上,所以與Spring Security結合使用會更加方便。
? ? ? ?我們只需要在配置中心的pom.xml中加入spring-boot-starter-security依賴,不需要做任何其他改動就能實現對配置中心訪問的安全保護。
????默認情況下,我們可以獲得一個名為user的用戶,并且在配置中心啟動的時候,在日志中打印該用戶的隨機密碼,具體如下:
????大多數情況下,我們并不會使用隨機生成密碼的機制。我們可以在配置文件中指定用戶和密碼,比如:
????由于我們已經為config-server設置了安全保護,如果這時候連接到配置中心的客戶端中沒有設置對應的安全信息,在獲取配置信息時會返回401錯誤。所以,需要通過配置的方式在客戶端中加入安全信息來通過校驗,比如:
????在微服務架構中,我們通常會采用DevOps的組織方式來降低因團隊間溝通造成的巨大成本,以加速微服務應用的交付能力。這就使得原本由運維團隊控制的線上信息將交由微服務所屬組織的成員自行維護,其中將會包括大量的敏感信息,比如數據庫的賬戶與密碼等。顯然,如果我們直接將敏感信息以明文的方式存儲于微服務應用的配置文件中是非常危險的。針對這個問題,Spring Cloud Config提供了對屬性進行加密解密的功能,以保護配置文件中的信息安全。比如下面的例子:
????在Spring Cloud Config中通過在屬性值前使用{cipher}前綴來標注該內容是一個加密值,當微服務客戶端加載配置時,配置中心會自動為帶有{cipher}前綴的值進行解密。通過該機制的實線,運維團隊就可以放心地將線上信息的加密資源給到微服務團隊,而不用擔心這些敏感信息遭到泄露了。下面我們來具體介紹如何在配置中心使用該項功能。
????在使用Spring Cloud Config的加密解密功能時,有一個必要的前提需要我們注意。為了啟用該功能,我們需要在配置中心的運行環境中安裝不限長度的JCE版本(Unlimited Strength Java Cryptography Extension)。雖然,JCE功能在JRE中自帶,但是默認使用的是長度限制的版本。我們可以從Oracle的官方網站下載到它,它是一個壓縮包,解壓后可以看到下面三個文件:
????我們需要將local_policy.jar和US_export_policy.jar兩個文件復制到$JAVA_HOME/jre/lib/security目錄下,覆蓋原來的默認內容。到這里,加密解密的準備工作就完成了。
????在完成了JCE的安裝后,可以嘗試啟動配置中心。在控制臺中,將會輸出一些配置中心持有的端點,主要包括如下幾個。
??????/encrypt/status:查看加密功能狀態的端點。
??????/key:查看密鑰的端點。
??????/encrypt:對請求的body內容進行加密的端點。
??????/decrypt:對請求的body內容進行解密的端點。
可以嘗試通過GET請求訪問/encrypt/status端點,我們將得到如下內容;
????該返回信息說明當前配置中心的加密功能還不能使用,因為沒有為加密服務配置對應的密鑰。
????我們可以通過encrpt.key屬性在配置文件中直接指定密鑰信息(對稱性密鑰),比如:
????加入上述配置信息后,重啟配置中心,再訪問/encrpt/status端點,我們將得到如下內容:????
????此時,我們配置中心的加密解密功能就已經可以使用了,不妨嘗試訪問一下/encrypt和/decrypt端點來使用加密和解密的功能。注意,這兩個端點都說POST請求,加密和解密信息需要通過請求體來發送。比如,以curl命令為例,我們可以通過下面的方式調用加密與解密端點:
????這里,我們通過配置encrypt.key參數來指定密鑰的實現方式采用了對稱性加密。這種方式實現起來比較簡單,只需要配置一個參數即可。另外,我們也可以使用環境變量ENCRYPT_KEY來進行配置,讓密鑰信息外部化存儲。
????在完成了上述驗證之后,確定配置服務中心已經正常運作,下面我們嘗試如何在服務應用中獲取上述 配置信息。
??????創建一個Spring Boot應用,命名config-client,并在pom.xml中引入下述依賴:
??????創建Spring Boot的應用主類,具體如下:
??????創建bootstrap.properties配置,來指定獲取配置文件的config-server位置,例如:
上述配置參數與Git中存儲的配置文件中各個部分的對應關系如下所示。
??????spring.application.name:對應配置文件規則中的{application}部分
??????spring.cloud.config.profile:對應配置文件規則中的{profile}部分
??????spring.cloud.config.label:對應配置文件中的{label}部分
??????spring.cloud.config.uri:配置中心config-server的地址
??????spring.cloud.config.discovery.enabled:使用注冊中心找尋config-server的地址
??????spring.cloud.config.discovery.serviceId:配置中心在注冊中心的applicationName
????這里需要格外注意,上面這些屬性必須配置在bootstrap.properties中,這樣config-server中的配置信息才能被正確加載。在第2章中,我們詳細說明了Spring Boot對配置文件的加載順序,對于本應用jar包之外的配置文件加載會優先與應用jar包內的配置內容,而通過bootstrap.properties對config-server的配置,使得該應用會從實現了外部化配置。
??????創建一個RESTful接口來返回配置中心的from屬性,通過@Value("from")綁定配置服務中配置的from屬性,具體實現如下:
??????除了通過@Value注解綁定注入之外,也可以通過Environment對對象來獲取配置屬性,比如:
????啟動config-client應用,并訪問http://localhost:8881/from,我們就可以根據配置內容輸出對應環境的from內容了。根據當前配置,我們可以獲得如下返回的內容:
????可以繼續通過修改bootstrap.properties中的配置內容獲取不同的配置信息來熟悉配置服務中的配置規則。
如果需要給我修改意見的發送郵箱:erghjmncq6643981@163.com
本博客的代碼示例已上傳GitHub:分布式配置中心https://github.com/erghjmncq6643981/SpringCloudConfig
資料參考:《Spring Cloud 微服務實戰》
轉發博客,請注明,謝謝。