在常規的開發中,每一個微服務都包含代碼和配置。其配置包含服務配置、各種開關和業務配置。若是系統結構中的微服務節點較少,那麼常規的代碼+配置的開發方式足以解決問題。當系統逐步迭代,其微服務會愈來愈複雜,慢慢演化成網狀依賴結構,這個時候常規的代碼+配置的開發方式就並不合適了,由於還要考慮總體系統的擴展性、伸縮性和耦合性等。這些問題中,配置的管理也是很是麻煩的。javascript
若是仍是以常規開發形式管理配置,則要承擔反覆修改編譯代碼、重啓系統、從新打包等風險。因此,一個能夠集中管理,帶有版本控制的配置中心應運而生。html
spring cloud config就是一個配置中心。其採用集中式管理每一個微服務的配置信息,並使用GIT等版本倉庫統一存儲配置內容,實現版本化管理控制。微服務與配置中心使用rest方式交互來實現可擴展的配置服務。java
spring cloud config配置中心解決了微服務系統的配置中心化、配置版本控制、平臺獨立、語言獨立等問題,其特性以下:mysql
使用spring cloud config配置中心,首先要搭建一個配置中心服務,配置中心服務自己也是一個spring cloud微服務。須要在Eureka註冊中心中發佈訂閱。須要依賴下述資源:jquery
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring cloud Eureka Client 啓動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
修改全局配置文件:git
spring.application.name=spring-cloud-config-server
server.port=10801
# Git repository configure # 遠程倉庫是用於集中管理各微服務配置文件的位置。配置文件須要經過GIT管理工具上傳到GIT倉庫中。配置文件命名有特有的規則,常見以下: # applicationName-profile.properties # applicationName-profile.yml # applicationName是被管理配置文件的微服務服務命名,即spring.application.name # profile是被管理配置文件的微服務環境,如開發環境的dev,測試環境的test等。若是未定義profile表明默認環境default。 # 配置Git遠程倉庫地址
spring.cloud.config.server.git.uri=https://github.com/kosamino/configs
# 若是遠程倉庫是公開的,不須要提供用戶名和密碼,若是是私有的,須要提供。
# spring.cloud.config.server.git.username= # spring.cloud.config.server.git.password=
提供啓動類:github
/** * @EnableConfigServer - 開啓配置中心,表明當前應用是一個配置中心服務端。 * 應用會根據全局配置文件訪問GIT遠程倉庫,並將遠程倉庫中的配置內容下載到本地。 */ @SpringBootApplication @EnableEurekaClient @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
瀏覽器測試配置文件內容獲取,從config server中獲取配置文件有固定的訪問方式。其格式爲:web
http://config-server-ip:port/applicationName/profile[/label]ajax
如:分支名稱是abc,applicationName是testApp,環境是dev,那麼請求路徑是:http://config-server-ip:port/testApp/dev/abcspring
從瀏覽器獲取的配置文件內容結果是JSON格式的字符串。內容大體以下:
{ "name":"spring-cloud-config-client", // 服務名稱 "profiles":["dev"], // 環境名稱 "label":null, // 分支名稱,默認爲master "version":"a600c404267f5341933ce02b3f62672ce5791b16", // 版本,由spring-cloud-config-server生成管理 "state":null, // 配置具體內容,從spring-cloud-config-server中獲取的配置必定包含default配置內容。
// 如:獲取環境配置文件路徑爲http://config-server-ip:port/spring-cloud-config-client/dev,
// 那麼不只會返回master分支的spring-cloud-config-client-dev.properties配置文件內容,還會返回master分支的spring-cloud-config-client.properties配置內容。 "propertySources":[ { "name":"https://gitee.com/kerwin_kim/test/spring-cloud-config-client-dev.properties", // 配置文件名,也是配置文件的遠程GIT訪問URL "source": // 配置文件內容 { "test.config.db.url":"jdbc:oracle:thin@localhost:1521:xe", "test.config.db.password":"oracle-password", "test.config.db.driver-class-name":"oracle.jdbc.driver.OracleDriver", "test.config.db.username":"oracle" } }, { //此處即爲默認配置文件spring-cloud-config-client.properties "name":"https://gitee.com/kerwin_kim/test/spring-cloud-config-client.properties", "source": { "test.config.db.url":"jdbc:mysql://localhost:3306/test", "test.config.db.password":"mysql-password", "test.config.db.driver-class-name":"com.mysql.jdbc.Driver", "test.config.db.username":"mysql" } } ] }
被管理配置文件的微服務稱爲spring-cloud-config-client,其自己就是一個普通的微服務,只是配置文件由spring-cloud-config-server集中管理。這個微服務應用必須依賴spring-cloud-starter-config啓動器資源,不然沒法訪問spring-cloud-config-server來獲取遠程配置內容。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
spring-cloud-config-client應用必須提供當前服務的基礎配置內容,如服務名稱、服務端口、Eureka註冊中心等,同時還必須配置訪問哪個spring-cloud-config-server。而當前應用的配置由被集中管理,因此全局配置文件命名修改成bootstrap.properties。bootstrap配置文件也是spring-cloud的全局配置文件,其加載優先級高於application系列全局配置文件,spring-cloud-config-client應用啓動的時候先根據bootstrap配置來設置本地應用最基礎的環境,再根據bootstrap中配置的遠程spring-cloud-config-server信息來遠程加載被集中管理的配置文件。
bootstrap.properties配置內容以下:
spring.application.name=spring-cloud-config-client
server.port=10803
# 配置config-client相關內容 # 開啓配置中心搜索
spring.cloud.config.discovery.enabled=true
# 配置config-server配置中心的服務名稱
spring.cloud.config.discovery.serviceId=spring-cloud-config-server
# 配置要從配置中心獲取的配置文件具體環境,默認爲default
spring.cloud.config.profile=dev
# 配置GIT中的分支名稱,默認爲master分支
spring.cloud.config.label=develop
Config-client客戶端服務應用,在啓動的時候,會根據bootstrap配置內容遠程訪問config-server,獲取當時的最新的配置內容。若是config-client運行過程當中,GIT倉庫中的配置內容發生變動,config-client不會自動的加載刷新配置內容。須要人爲干預。
在工做中,不能由於配置的變動頻繁重啓各微服務,這樣會嚴重影響系統的服務能力。那麼在配置變化後,如何讓微服務熱刷新配置內容是一個必須解決的問題。對spring-cloud-config-server來講,GIT遠程倉庫中的配置內容是否發生變化都是無所謂的,由於當spring-cloud-config-client訪問spring-cloud-config-server獲取配置內容時,spring-cloud-config-server都會嘗試訪問GIT,獲取最新的配置內容。
而spring-cloud-config-client應用在啓動後,須要提供不重啓服務從新加載遠程配置邏輯。這個熱屬性配置內容的功能須要依賴spring-boot-starter-actuator啓動器來實現。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
在actuator組件中,提供了refresh功能,這個功能就是刷新應用環境。可是spring-cloud默認關閉了這個功能的訪問權限,須要在bootstrap配置文件中開啓refresh訪問權限。
management.security.enabled=false
actuator組件提供的refresh功能要求必須使用http協議訪問,且必須是POST請求。方式任意(如postmam或者thymeleaf發送http請求)。以下經過thymeleaf刷新客戶端示例:
引入依賴:
<!-- thymeleaf啓動器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
新增Controller:
@Controller public class DispatcherController { @GetMapping("/") public String test(){ return "index"; } }
新增頁面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>熱刷新應用配置</title> <script type="text/javascript" src="jquery-1.9.1.js"></script> <script type="text/javascript"> /* * 注意: AJAX默認是不能作跨域訪問的。當前的代碼是爲當前應用提供服務的。 * 若是須要跨域訪問,建議使用HTTPClient技術實現。或其餘技術實現。 */ function refresh(){ var url = $("#urlValue").val(); $.ajax({ 'url' : url, 'type': 'post', 'success' : function(data){ alert(data); } }); } </script> </head> <body> <center> 刷新工程:<input type="text" name="urlValue" id="urlValue"/> <input type="button" value="刷新" onclick="refresh();"> </center> </body> </html>
瀏覽器輸入連接訪問:http://ip:port/;再界面中輸入/refresh點擊「刷新」按鈕便可,如圖。
配置文件統一存儲在GIT中,雖然加強了管理和版本控制,可是文件內容的安全性也成了問題。若是外部任意知道了GIT地址或spring-cloud-config-server的地址,那麼配置文件的所有內容至關於對外徹底開放。這個時候就須要提供配置內容加解密邏輯。將加密後的配置內容保存在GIT倉庫中,在spring-cloud-config-client訪問spring-cloud-config-server的時候,經過spring-cloud-config-server實現加密數據的解密,這樣就能夠保證配置內容的安全了。
在spring-cloud架構中,Dalston.SR1版本能夠正常提供加解密功能,而其餘版本有BUG,沒法提供加解密功能。使用加解密邏輯,須要在JDK/JRE環境中增長policy支持。須要在oracle官網上下載policy相關資源,地址爲:
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
將下載的兩個policy相關jar包複製到$JAVA_HOME/lib/和$JAVA_HOME/jre/lib/目錄中便可。當環境準備完畢後,能夠經過下述地址測試spring-cloud-config-server的加解密環境是否生效:
http://config-server-ip:port/encrypt/status
使用相同的密鑰(key)實現配置數據的加密和解密。在spring-cloud-config-server的全局配置文件中增長密鑰配置:
# 對稱加密:配置密鑰內容,加密和解密都使用這個密鑰
encrypt.key=test
spring-cloud-config-server提供了對數據加密和解密的處理邏輯,要求必須使用POST請求方式,在請求體中直接傳遞要加解密的內容(json請求參數提交方式),spring-cloud-config-server會返回加解密後的結果。
在GIT倉庫中保存的密文數據使用前綴{cipher}表示數據爲密文,如:
test.config.db.username={cipher}bb605c7e4bac3ae59ed83860a164b58b48fa1b08012ccaf49eb0b67825fefc33
test.config.db.password={cipher}7e97d20fe476b5268da4fea3dec63636f96e0dde4357acda4e614d33fcda1cfd
這樣spring-cloud-config-client經過spring-cloud-config-server獲取配置數據的時候,spring-cloud-config-server會自動實現解密過程,並將解密後的結果返回給spring-cloud-config-client。
一樣採用thymeleaf,引入依賴spring-boot-starter-thymeleaf,而後添加Controller,
@Controller public class DispatcherController { @GetMapping("/") public String test(){ return "index"; } }
添加頁面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>加解密測試工具</title> <script type="text/javascript" src="jquery-1.9.1.js"></script> <script type="text/javascript"> function encrypt(){ var source = $("#sourceEncrypt").val(); $.ajax({ 'url' : 'http://localhost:10801/encrypt', 'type': 'post', 'data': source, 'success' : function(data){ $("#singEncrypt").val(data); } }); } function decrypt(){ var sing = $("#singDecrypt").val(); $.ajax({ 'url' : 'http://localhost:10801/decrypt', 'type': 'post', 'data': sing, 'success' : function(data){ $("#sourceDecrypt").val(data); } }); } </script> </head> <body> <div style="padding-left: 100px; padding-right: 100px"> 明文:<input type="text" name="urlValue" id="sourceEncrypt" style="width: 300px"/> <br> 密文:<input type="text" name="urlValue" id="singEncrypt" style="width: 300px"/> <input type="button" value="加密" onclick="encrypt();"> <hr> 密文:<input type="text" name="urlValue" id="singDecrypt" style="width: 300px"/> <br> 明文:<input type="text" name="urlValue" id="sourceDecrypt" style="width: 300px"/> <input type="button" value="解密" onclick="decrypt();"> </div> </body> </html>
示例如圖:
使用不一樣的密鑰(key)實現配置數據的加密和解密。使用公鑰加密數據,使用私鑰解密數據。
JDK提供了java-keytool工具來生成密鑰,生成的密鑰信息是"keystore"文件。keytool工具使用方式以下:
keytool -genkeypair -alias "test" -keyalg "RSA" -keystore "test.keystore" 輸入密鑰庫口令: 123456 再次輸入新口令: 123456 您的名字與姓氏是什麼? [Unknown]: test 您的組織單位名稱是什麼? [Unknown]: test 您的組織名稱是什麼? [Unknown]: test 您所在的城市或區域名稱是什麼? [Unknown]: beijing 您所在的省/市/自治區名稱是什麼? [Unknown]: beijing 該單位的雙字母國家/地區代碼是什麼? [Unknown]: cn CN=test, OU=test, O=test, L=beijing, ST=beijing, C=cn是否正確? [否]: y 輸入 <test> 的密鑰口令test123 (若是和密鑰庫口令相同, 按回車): 再次輸入新口令:test123
將生成的密鑰文件test.keystore保存到spring-cloud-config-server的classpath下(src/main/resources),並在全局配置文件中增長下述內容:
# 密鑰文件保存路徑 encrypt.key-store.location=classpath:test.keystore # 密鑰別名,此別名是公開的 encrypt.key-store.alias=test # 密鑰庫口令 encrypt.key-store.password=123456 # 密鑰口令 encrypt.key-store.secret=test123
實現配置數據的加解密方式和對稱加密一致。
如今配置內容經過加解密的方式實現了安全保障,可是配置中心spring-cloud-config-server仍是徹底對外公開的。若是經過http協議地址:
http://config-server-ip:port/applicationName/profile/label
仍是能夠獲取到配置文件內容,這對配置信息的安全保護仍是不足。須要給spring-cloud-config-server提供用戶安全認證,在這裏使用spring-boot提供的security組件來使用HTTP Bastic用戶安全認證。
在spring-cloud-config-server中增長新的依賴security:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
在spring-cloud-config-server全局配置文件中增長用戶信息:
security.basic.enabled=true
security.user.name=test
security.user.password=123456
這樣就爲spring-cloud-config-server提供了用戶安全認證。
在客戶的bootstrap配置文件中增長下述內容,來實現spring-cloud-config-client訪問spring-cloud-config-server的用戶登陸:
spring.cloud.config.username=test
spring.cloud.config.password=123456
若是要想加載多個指定配置文件,並且此配置文件不是以spring.application.name命名規則命名的配置文件,則能夠在config-client端配置文件:
# 加載多個配置文件,指定配置文件命名,再也不使用spring.application.name查找配置文件,而是使用配置內容找指定的配置文件。經常使用。是配置在client端
spring.cloud.config.name=fileName1,fileName2
若是要加載多目錄下的配置文件,則須要在config-server端添加配置:
# 檢索多個目錄中的同名配置文件,經常使用於分目錄管理多環境配置文件的狀況下,如:dir1中保存default配置,dir2中保存dev配置,dir3中保存test配置。
# 配置文件命名規則不變,默認仍是applicationName[-profile].properties。是配置在server端的。
spring.cloud.config.server.git.search-paths=dir1,dir2,dir3
Spring Cloud Bus集成了市面上常見的RabbitMQ和Kafka等消息代理。其會鏈接微服務系統中全部擁有Bus總線機制的節點,當有數據變動的時候,會經過消息中間件使用消息廣播的方式通知全部的微服務節點同步更新數據。(如:微服務配置更新等)
這種實現方案在設計上並非很是合適,比較刷新功能和某一個config client微服務耦合到了一塊兒。可做爲了解。
實現Bus刷新功能須要在全部的config client端應用中增長spring-cloud-starter-bus-amqp依賴,這個依賴是消息總線集成的RabbitMQ消息同步組件。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
在config client端的bootstrap配置文件中增長RabbitMQ相關配置:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=test
spring.rabbitmq.password=123456
Bus消息總線會提供一個/bus/refresh服務來實現應用的熱刷新。再也不使用actuator來提供人刷新邏輯。/bus/refresh服務要求請求必須是post請求,可用任意技術實現。
這裏要注意,雖然刷新服務的邏輯由Bus消息總線來提供,可是/bus/refresh服務默認仍是關閉的,須要開啓,在config client端的bootstrap配置文件中必須配置下述內容:
management.security.enabled=false
這種實現方案在設計上更加完美,將熱刷新邏輯和具體的服務應用解耦。推薦使用。實現過程和2.1一致。須要config server和config client端都引入Bus消息總線組件。
使用Bus總線也能夠實現部分服務熱刷新。前文講到的兩種刷新實現方式都是全局熱刷新,全部帶有Bus消息總線組件且與同一個RabbitMQ鏈接的微服務應用都會執行熱刷新邏輯。有些時候,熱刷新只須要在一個微服務應用或一個微服務應用集羣中執行,這個時候就須要使用局部刷新功能。
局部熱刷新的實現過程和全局熱刷新一致,只是請求地址/bus/refresh須要提供一個GET請求參數,請求參數命名爲destination,參數值是要刷新的微服務名稱:端口[**]。如:
微服務名: config-client
微服務端口: 1080二、1080三、10804
若是隻熱刷新微服務config-client:10802時,請求的路徑爲/bus/refresh?destination=config-client:10802。
若是須要刷新config-client微服務集羣,則請求路徑爲/bus/refresh?destination=config-client:**