本文內容導航:
2.2.通過創建一個映射遠程配置信息的Bean(RemoteConfigProperties) 方式獲取配置信息
上篇總結了熔斷降級(Hystrix)與監控的使用方式,里面涉及到的一些重點的知識細節我都有說明,網上很多的spring cloud教程只是簡單的貼代碼、簡單描述,而我的文章是要體現知識的積累與開發時的參考,故花了一些時間描述了一些細節,這些細節能幫助我自己(夢在旅途)以及大家少走彎路,本文仍然圍繞這個中心思想來總結描述關於配置中心的搭建與使用,實現了多種搭建配置服務中心(config server),也實現了多種配置消費客戶端的實現方式。(多種配置消費方式目前很少有人總結,而我在本文中有特別介紹,不一定都用得上,但應該要了解,以便舉一反三靈活運用),好了廢話不多說,直接開始本文主題。
首先我們要了解,為什么需要配置中心?配置中心能解決什么問題?回答這些問題可以參見:https://www.cnblogs.com/xiaoqi/p/configserver-compair.html,我這里簡要說明一下:JAVA應用一般常用的配置文件是:xxx.Properties 或 xxx.yml文件,.NET(.NET CORE)一般常用的配置文件是:xxx.config 或 xxx.json 甚至是xxx.ini,它們都能夠很好的應對單機本地配置的需求(可以多個應用共用同一個目錄位置的配置文件),但如果是分布式應用,那么本地配置文件在某些場景下就顯得不那么合適了,因為一是存在配置冗余(不同機器上配置相同的配置信息),二是無法快速同步配置信息(如果要更改配置信息,就得每台服務器一台一台的更改,或使用文件同步機制稍微能保證盡快同步),三是沒有多版本管理,出現更新配置后有問題無法快速回滾(如有問題,需重新配置或人為將之前的備份的配置文件一台台服務器更新或文件同步到多台服務器),比如:數據庫連接字符串,如果使用本地配置就存在如上說的三點問題,正因為存在這些問題,所以才有了分布式配置中心框架來解決這些問題。
分布式配置中心的主流框架有很多,由於本文是總結spring cloud系列的文章,故這里通過DEMO來實現分布式配置中心Server端及Client消費端(讀取配置),並確保高可用。
一、搭建配置服務中心(config server)
首先通過IDEA spring initializer(或直接通過https://start.spring.io/)創建一個spring boot項目(demo項目命名:configserver),創建過程中選擇:config server依賴,生成項目后的POM XML文件如下:(如果缺少依賴請自行手動添加)

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.zuowenjun.cloud</groupId> <artifactId>configserver</artifactId> <version>0.0.1-SNAPSHOT</version> <name>configserver</name> <url>http://www.zuowenjun.cn</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> </properties> <dependencies> <!--spring cloud配置中心依賴[默認GIT]--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
然后下面分別介紹通過三種方式來實現配置中心:(注意三種方式均使用上面的項目初始環境,下面不再介紹)
1.1.git方式(即:將GIT倉庫【GITHUB、GITLAB、 GITEE等】作為配置集中存儲的介質)
前提工作:既然是使用GIT倉庫作為配置存儲,那我們首先就得在相關的GIT站點上創建用於存放config信息的倉庫,如我(夢在旅途)在自己的github上創建了一個learning-demos/config倉庫目錄,然后在config目錄下分別創建兩個環境的配置文件(http://configclient-dev.properties、http://configclient-prod.properties),配置文件命名規則應盡可能使用:{application}-{profile}.{properties|yml},如下是GIT中兩個文件配置的演示內容:(演示倉庫地址:https://github.com/zuowj/learning-demos)

--configclient-dev.properties內容:測試環境 demo-config-profile-env=dev zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190226.AAAXX66 zuowenjun.skills=.net,java,html,js,css,sql,python,vb--20190226.BBBXX66 zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;--20190226.CCCXX1 --configclient-prod.properties內容:生產環境 demo-config-profile-env=prod zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com,http://github.com/zuowj zuowenjun.skills=.net,java,html,js,css,sql,python,vb zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;學無止境;機會是留給有准備的人
1.1.1.在spring boot啟動類(ConfigserverApplication)上添加@EnableConfigServer注解,代碼如下:

package cn.zuowenjun.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class ConfigserverApplication { public static void main(String[] args) { SpringApplication.run(ConfigserverApplication.class, args); } }
1.1.2.在配置文件(application.yml,當然也可以是application.properties看個人喜好,本系列均采用yml)中進行如下配置,重點關注spring.cloud.config節點,相關配置參數有注釋說明:
spring:
application:
name: configserver
profiles:
active: git #設置使用本地配置(默認是git,可以設置:subversion(SVN),native(本地))
cloud:
config:
server:
#如下是GIT配置
git:
uri: https://github.com/zuowj/learning-demos # 配置git倉庫的地址(最后不需要帶/,否則會出現:No custom http config found for URL: XXX)
search-paths: config # git倉庫地址下的相對搜索地址(可用使用通配符),可以配置多個,用,分割。可以{application}實現按應用查配置
username: # git倉庫的賬號(公開倉庫無需賬號信息)
password: # git倉庫的密碼(公開倉庫無需賬號信息)
default-label: master #git默認分支
通過上述簡單的兩步即完成搭建基於GIT的配置服務中心,啟動運行項目,GIT倉庫中的配置文件會被自動轉換成當前項目的web api,若需訪問查看遠程配置數據可以參照以下的規則:
/{application}/{profile}[/{label}]
[/{label}]/{application}-{profile}{.yml|.properties|.json}
規則簡單說明:{application}=配置消費方應用名稱(即:config client的項目名,通俗講:就是誰用這個配置就是誰的名字),{profile}=配置環境(如:dev開發環境,test測試環境,prod生產環境),{label}=倉庫分支名(git或svn方式指定,native本地方式無需指定),.yml|.properties|.json表示指定的響應返回格式,{}表示必需,[]表示可選,|表示或的關系,例如本Demo項目訪問:
http://localhost:8866/configclient/dev 、http://localhost:8866/configclient/dev/master
http://localhost:8866/configclient-dev.yml、http://localhost:8866/configclient-dev.properties、http://localhost:8866/configclient-dev.json、http://localhost:8866/master/configclient-dev.yml
最終可以根據訪問URL規則返回相應的內容,類似如下:

//request: http://localhost:8866/configclient-dev.yml demo-config-profile-env: dev zuowenjun: motto: Learning is endless; Opportunity is for the prepared mind;--20190226.CCCXX1 site: http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190226.AAAXX66 skills: .net,java,html,js,css,sql,python,vb--20190226.BBBXX66 //request: http://localhost:8866/configclient-dev.properties demo-config-profile-env: dev zuowenjun.motto: Learning is endless; Opportunity is for the prepared mind;--20190226.CCCXX1 zuowenjun.site: http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190226.AAAXX66 zuowenjun.skills: .net,java,html,js,css,sql,python,vb--20190226.BBBXX66 //request: http://localhost:8866/configclient-dev.json {"demo-config-profile-env":"dev","zuowenjun":{"motto":"Learning is endless; Opportunity is for the prepared mind;--20190226.CCCXX1","site":"http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190226.AAAXX66","skills":".net,java,html,js,css,sql,python,vb--20190226.BBBXX66"}} //request: http://localhost:8866/configclient/dev {"name":"configclient","profiles":["dev"],"label":null,"version":"d4616a65c8429b9dd3a67ff226b125ae6a0959bb","state":null,"propertySources":[{"name":"https://github.com/zuowj/learning-demos/config/configclient-dev.properties","source":{"zuowenjun.skills":".net,java,html,js,css,sql,python,vb--20190226.BBBXX66","zuowenjun.site":"http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190226.AAAXX66","demo-config-profile-env":"dev","zuowenjun.motto":"Learning is endless; Opportunity is for the prepared mind;--20190226.CCCXX1"}}]}
可以看到GIT中的配置信息通過config server的API能夠正常返回,表示搭建成功,另外需要注意的是如果配置文件中包含中文,那么可能中文返回的就是亂碼了,上面如果瀏覽prod生產環境【生產環境我特意配置了中文】,如:http://localhost:8866/configclient/prod,那么中文就會亂碼,解決方案后面會說明,此處了解有這個情況即可。
1.2.svn方式(即:將SVN倉庫作為配置集中存儲的介質)
前提工作:如同GIT方式一樣,要使用SVN倉庫作為配置存儲,那就需要SVN服務器,將創建用於存放config信息的倉庫,如我(夢在旅途)在SVN服務器創建了app-config倉庫目錄,然后在trunk主干中仍然添加兩個配置文件(dev,prod)與GIT方式文件名相同,內容如下(內容與GIT方式也基本相同,只是稍微改變內容以便區分):(演示倉庫地址:http://svnhost:port/svn/app-config/trunk)

--configclient-dev.properties內容: demo-config-profile-env=dev-svn zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190227 zuowenjun.skills=.net,java,html,js,css,sql,python,vb--20190227 zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;--20190227 --configclient-prod.properties內容: demo-config-profile-env=prod-svn zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com,http://github.com/zuowj zuowenjun.skills=.net,java,html,js,css,sql,python,vb zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;學無止境;機會是留給有准備的人
1.2.1.由於GIT是默認的實現方式,當引入spring-cloud-config-server時,它就具備了相關的框架處理能力,而這里使用SVN,就需要在POM XML中除了添加config-server依賴外還需單獨添加SVN實現方式的依賴,配置如下:
<!--spring cloud配置中心依賴[默認GIT]--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <!--使用SVN作為配置中心依賴--> <dependency> <groupId>org.tmatesoft.svnkit</groupId> <artifactId>svnkit</artifactId> </dependency>
1.2.2.在配置文件(application.yml)中進行如下配置,仍然重點關注spring.cloud.config節點,相關配置參數有注釋說明:
server:
port: 8866
spring:
application:
name: configserver
profiles:
active: subversion #設置使用本地配置(默認是git,可以設置:subversion(SVN),native(本地))
cloud:
config:
server:
#如下是SVN配置
svn:
uri: http://svnhost:port/svn/app-config #SVN倉庫地址
username: svnuser #SVN賬號(如果沒有權限可為空)
password: svnpassword #SVN密碼(如果沒有權限可為空)
default-label: trunk #默認SVN分支
通過上述簡單的兩步即完成搭建基於SVN的配置服務中心,啟動運行項目,SVN倉庫中的配置文件會被自動轉換成當前項目的web api,訪問路徑與GIT方式完全相同在此不重述。(參照1.1.2訪問URL,可以得到SVN倉庫trunk分支里配置文件配置信息的內容)
1.3.本地文件方式(即:將config server項目所有服務器的本地文件存放目錄作為配置集中存儲的介質)
1.3.1.先在config server項目文件服務器創建指定存放配置的目錄,如本demo是直接創建在項目的resources/configs目錄,仍然是創建與GIT相同的兩個配置文件(dev、prod),配置內容如下:

--configclient-dev.properties內容: demo-config-profile-env=dev-native zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com--20190227 zuowenjun.skills=.net,java,html,js,css,sql,python,vb--20190227 zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;--20190227 --configclient-prod.properties內容: demo-config-profile-env=prod-native zuowenjun.site=http://www.zuowenjun.cn,http://zuowj.cnblogs.com,http://github.com/zuowj zuowenjun.skills=.net,java,html,js,css,sql,python,vb zuowenjun.motto=Learning is endless; Opportunity is for the prepared mind;學無止境;機會是留給有准備的人
1.3.2.在配置文件(application.yml)中進行如下配置,仍然重點關注spring.cloud.config節點,相關配置參數有注釋說明:(很簡單,只需要設置profiles.active=native,然后在native節點設置查找配置的存放目錄即可)
server: port: 8866 spring: application: name: configserver profiles: active: native #設置使用本地配置(默認是git,可以設置:subversion(SVN),native(本地)) cloud: config: server: #如下是本地文件配置 native: search-locations: classpath:/configs #配置文件存放的目錄
通過上述簡單的兩步即完成搭建基於本地文件的配置服務中心,啟動運行項目,存放的配置文件會被自動轉換成當前項目的web api,訪問路徑與GIT方式完全相同在此不重述。(參照1.1.2訪問URL,可以得到本地文件配置信息的內容)
1.4.解決配置中包含中文內容返回亂碼問題(此處實現方式參照網上說明,在此只是集中說明,便於大家理解)--均適用於以上三種方式
亂碼原因及解決方案思路可參見:https://blog.csdn.net/sinat_38843093/article/details/79960777?utm_source=blogxgwz4,但由於最新版本的spring boot的PropertySourceLoader接口定義有些不同故我這里參照該文進行完善並最終解決亂碼。
首先自定義一個實現了PropertySourceLoader的類:MyPropertySourceLoader,並實現接口的方法邏輯,具體代碼如下:(最重要的代碼是:properties.load(new InputStreamReader(inputStream, StandardCharsets.UTF_8));即表示使用UTF8的字符集來讀取配置文件流信息)

package cn.zuowenjun.cloud; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.env.PropertySourceLoader; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Properties; public class MyPropertySourceLoader implements PropertySourceLoader { private Logger logger= LoggerFactory.getLogger(MyPropertySourceLoader.class); @Override public String[] getFileExtensions() { return new String[]{"properties", "xml"}; } @Override public List<PropertySource<?>> load(String name, Resource resource) throws IOException { Properties properties=new Properties(); InputStream inputStream=null; try{ inputStream=resource.getInputStream(); properties.load(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); }catch (IOException ioEx){ logger.error("load inputStream failed",ioEx); throw ioEx; } catch (Exception e){ logger.error("load inputStream failed",e); }finally { if(inputStream!=null){ inputStream.close(); } } List<PropertySource<?>> propertySourceList=null; if (!properties.isEmpty()) { PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(name, properties); propertySourceList= new ArrayList<>(); propertySourceList.add(propertiesPropertySource); } return propertySourceList; } }
然后在resources目錄下創建META-INF目錄,然后在該目錄中創建spring.factories文件(這里spring boot的擴展配置文件),在該文件中輸入如下內容即可:
org.springframework.boot.env.PropertySourceLoader=cn.zuowenjun.cloud.MyPropertySourceLoader
若想了解spring.factories相關信息,可參考:https://blog.csdn.net/lvoyee/article/details/82017057 、 https://blog.csdn.net/boling_cavalry/article/details/83048588
通過上述步驟,現在無論是哪種方式訪問配置中心的API,若涉及中文都能正常返回不會亂碼,如下圖所示:(當然如果使用[/{label}]/{application}-{profile}{.yml|.properties|.json}這種方式訪問可能還會出現亂碼,具體原因我還未了解,求大神們指點,但目前已不影響配置消費【即:配置消費方獲取到的中文信息均是正常不會亂碼】)
二、搭建配置消費客戶端(config client)
首先通過IDEA spring initializer(或直接通過https://start.spring.io/)搭建spring boot + spring MVC(Rest API)空項目,創建過程中選擇:web、config client依賴,最后生成的POM XML文件如下:(如果缺少依賴請自行手動添加)

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.zuowenjun.cloud</groupId> <artifactId>configclient</artifactId> <version>0.0.1-SNAPSHOT</version> <name>configclient</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> </properties> <dependencies> <!--spring MVC依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--config client依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!--ConfigurationProperties類所需依賴,手動添加的--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
然后在resources目錄下創建bootstrap.yml文件,並在配置文件中添加如下內容:(如果想了解bootstrap與application配置文件兩者的區別,請參見:https://www.cnblogs.com/BlogNetSpace/p/8469033.html)
server:
port: 8008
spring:
application:
name: configclient
cloud:
config:
name: configclient #對應config server Url中的{application}
profile: prod #配置環境,對應config server Url中的{profile}
#label: trunk #配置分支(不配置則默認:git則是master,svn則是trunk),
uri: http://localhost:8866 #配置中心地址
最后下面分別介紹實現各種不同的配置消費(讀取配置)方式:(注意均使用上面的項目初始環境,同時以下的幾種服務配置消費方式均可同時並存在一個項目中)
2.1.通過@value方式獲取配置信息
這種最為簡單,網上也大部份是這種,我這里為了便於區分配置屬性與controller本身使用了繼承的方式(注意,我這里用繼承而沒有使用單獨定義一個RemoteConfig配置類+Value,是因為經我驗證發現將會影響配置自動刷新,具體原因還未查清,初步判斷與加上@RefreshScope注解上生成的代理類有關系),代碼結構更清晰,實現代碼如下:
package cn.zuowenjun.cloud.model; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; public abstract class RemoteConfig { @Value("${demo-config-profile-env}") public String profileEnv; @Value("${zuowenjun.site}") public String zwjSite; @Value("${zuowenjun.skills}") public String zwjSkills; @Value("${zuowenjun.motto}") public String zwjMotto; } package cn.zuowenjun.cloud.Controller; import cn.zuowenjun.cloud.model.RemoteConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController extends RemoteConfig { @RequestMapping("/config/byval") public Object getRemoteConfigByValue(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","@Value"); Map<String,String> remoteCgfMap=new HashMap<>(); remoteCgfMap.put("profileEnv", this.profileEnv); remoteCgfMap.put("zwjSite", this.zwjSite); remoteCgfMap.put("zwjSkills",this.zwjSkills); remoteCgfMap.put("zwjMotto", this.zwjMotto); model.put("remoteConfig",remoteCgfMap); return model; } }
如上代碼所示,我們只需在類成員字段上標記@Value注解,並指定要獲取的配置中心上配置項的名稱即可。運行項目,訪問:http://localhost:8008/demo/config/byval,即可響應返回獲取到的config信息,如下圖示:
2.2.通過創建一個映射遠程配置信息的Bean(RemoteConfigProperties) 方式獲取配置信息
首先定義一個映射遠程配置信息的Bean類:RemoteConfigProperties,該類中所有的屬性名均需與配置中心上配置文件的配置內容相同(名字以及層級都需相同,若配置項名稱有-,則配置類中請忽略直接拼接命名即可但需符合lowerCamelCase,如:demo-config-profile-env,則字段名為:demoConfigProfileEnv,如果屬性中有點( . ),則應視為點后面的部分為下級,應再定義相關的配置映射類),該類完整代碼如下:
package cn.zuowenjun.cloud.model; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties()//如果有前綴,則可以設置prefix=XXX public class RemoteConfigProperties { private String demoConfigProfileEnv; private Zuowenjun zuowenjun; public String getDemoConfigProfileEnv() { return demoConfigProfileEnv; } public void setDemoConfigProfileEnv(String demoConfigProfileEnv) { this.demoConfigProfileEnv = demoConfigProfileEnv; } public Zuowenjun getZuowenjun() { return zuowenjun; } public void setZuowenjun(Zuowenjun zuowenjun) { this.zuowenjun = zuowenjun; } public static class Zuowenjun { private String site; private String skills; private String motto; public String getSite() { return site; } public void setSite(String site) { this.site = site; } public String getSkills() { return skills; } public void setSkills(String skills) { this.skills = skills; } public String getMotto() { return motto; } public void setMotto(String motto) { this.motto = motto; } } }
如上代碼所示,屬性字段名與配置項的命名保持一致,若有下級則定義下級的配置類,這里下級配置類(Zuowenjun)采用了內部靜態類的方式(注意這里如果是內部類,一定是static,而不能是普通的class,因為內部普通類與內部靜態類是有區別的,具體可參見:java 內部類和靜態內部類的區別),也可以單獨定義一個類,同時在配置類上標記了@ConfigurationProperties(這指明了該類是配置映射類),@Component這個不用說了吧就是表示能自動被spring IOC容器掃描並注冊。
然后在DemoController中增加字段注入該類:RemoteConfigProperties,並添加用於讀取RemoteConfigProperties類中的配置信息的方法(getRemoteConfigByPropertiesBean),具體代碼如下:(這里保留了@Value方式的代碼)

package cn.zuowenjun.cloud.Controller; import cn.zuowenjun.cloud.model.RemoteConfig; import cn.zuowenjun.cloud.model.RemoteConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController extends RemoteConfig { @Autowired private RemoteConfigProperties remoteConfigProperties; @RequestMapping("/config/byval") public Object getRemoteConfigByValue(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","@Value"); Map<String,String> remoteCgfMap=new HashMap<>(); remoteCgfMap.put("profileEnv", this.profileEnv); remoteCgfMap.put("zwjSite", this.zwjSite); remoteCgfMap.put("zwjSkills",this.zwjSkills); remoteCgfMap.put("zwjMotto", this.zwjMotto); model.put("remoteConfig",remoteCgfMap); return model; } @RequestMapping("/config/byprops") public Object getRemoteConfigByPropertiesBean(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","Properties Bean"); model.put("remoteConfig",remoteConfigProperties); return model; } }
最后啟動運行項目,訪問:http://localhost:8008/demo/config/byprops,即可響應返回獲取到的config信息,如下圖示:
2.3.通過Environment來直接獲取配置信息
其實上面2種方式底層都是使用這種方式來獲取配置信息的只是包裝了后大家無需關心而矣,核心是通過environment.getProperty方法來獲取對應的配置信息的,這種方式只需在DemoController增加字段注入Environment的實例,然后添加一個從Environment獲取配置信息的方法即可(getRemoteConfigByEnv),具體代碼如下:(保留@Value方式、映射遠程配置信息的Bean(RemoteConfigProperties) 方式)

package cn.zuowenjun.cloud.Controller; import cn.zuowenjun.cloud.model.RemoteConfig; import cn.zuowenjun.cloud.model.RemoteConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController extends RemoteConfig { @Autowired private Environment environment; @Autowired private RemoteConfigProperties remoteConfigProperties; @RequestMapping("/config/byval") public Object getRemoteConfigByValue(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","@Value"); Map<String,String> remoteCgfMap=new HashMap<>(); remoteCgfMap.put("profileEnv", this.profileEnv); remoteCgfMap.put("zwjSite", this.zwjSite); remoteCgfMap.put("zwjSkills",this.zwjSkills); remoteCgfMap.put("zwjMotto", this.zwjMotto); model.put("remoteConfig",remoteCgfMap); return model; } @RequestMapping("/config/byprops") public Object getRemoteConfigByPropertiesBean(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","Properties Bean"); model.put("remoteConfig",remoteConfigProperties); return model; } @RequestMapping("/config/byenv") public Object getRemoteConfigByEnv(){ Map<String,Object> model=new HashMap<>(); model.put("getMode","Environment"); Map<String,String> remoteCgfMap=new HashMap<>(); remoteCgfMap.put("profileEnv", environment.getProperty("demo-config-profile-env")); remoteCgfMap.put("zwjSite", environment.getProperty("zuowenjun.site")); remoteCgfMap.put("zwjSkills", environment.getProperty("zuowenjun.skills")); remoteCgfMap.put("zwjMotto", environment.getProperty("zuowenjun.motto")); model.put("remoteConfig",remoteCgfMap); return model; } }
最后啟動運行項目,訪問:http://localhost:8008/demo/config/byenv,即可響應返回獲取到的config信息,如下圖示:
如上三種方式配置文件均不需要改變,如果需要訪問不同的環境,不同的分支,則可以修改項目配置文件bootstrap.yml中對應的屬性:profile、label等,至於配置中心(config server)使用的是哪種方式存儲配置,配置消費端(config client)無需關心,只管用即可。
2.4.實現配置信息自動刷新(可參見:)
2.4.1.在controller類(DemoController)上添加@RefreshScope注解,簡單就不貼代碼
2.4.2.在POM XML中添加actuator依賴,配置如下:
<!--actuator監控功能所需依賴(內部包含refresh,動態刷新配置信息需要)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2.4.3.在項目配置文件(bootstrap.yml或application.yml都可以)添加如下配置內容:
management: endpoints: web: exposure: include: "*" #暴露所有端口,也可以指定某一個環境(先management.endpoint.{profile}.enabled=true,然后這里指定這個{profile},多個用,分隔)
通過上述兩步即完成config client端的自動監聽與刷新機制(刷新原理參見:https://blog.csdn.net/cml_blog/article/details/78411312)
這時可以啟動運行項目,然后POST請求訪問:http://localhost:8008/actuator/refresh,如果能正常響應結果沒有報404就OK了,響應內容為空?沒關系,因為只有當config server端的配置文件有更新,這時POST這個API時,即會檢測並響應返回更新的配置項信息,這樣config client內部即可進行配置的刷新工作。到目前還沒有實現全自動刷新配置,因為config server端配置文件有更新,config client端並不會第一時間感知到,如果不去POST請求refresh API則不會更新,所以自動更新的關鍵是如何能夠實現配置中心的配置文件一旦更改就能通知所有的配置消費端(config client)自動更新配置信息。如果采用github的方式,那么可以使用github倉庫的Webhooks功能,具體操作位置如下圖示:
設置webhooks后,只要這個存配置的GIT倉庫發生改變,將針觸發事件並回調refresh接口(當然可以封裝一個通用接口,然后里面把所有的 config client都調refresh接口),config client端收到刷新請求后就會重新從config server中獲取配置信息。
雖然webhooks能夠解決自動刷新問題,但仍不夠優美,比較好的實現方式是再結合spring cloud bus、mq實現更好的自動刷新所有config client,具體可參見:https://www.cnblogs.com/willpan-z/p/9483674.html 、 https://blog.csdn.net/wtdm_160604/article/details/83720391
另外actuator還默認集成了健康檢查功能,可訪問:http://localhost:8866/actuator/health,即可看到響應結果,正常則為status=UP,否則可能是DOWN
2.5.加入到注冊中心配置成高可用集群環境
思路:我們可以把服務配置中心(config server)與服務消費客戶端(config client)均作為服務加入到注冊中心里面,然后服務消費客戶端(config client)通過注冊中心根據服務配置中心(config server)的服務ID(應用名稱)找到config server IP,然后請求該IP即可。
前提:先把之前文章《玩轉Spring Cloud之服務注冊發現(eureka)及負載均衡消費(ribbon、feign)》中的eureka server項目正常啟動;
2.5.1.改造服務配置中心(config server),集成eureka client(這個我之前文章有說明)【操作步驟簡要重述:先在POM XML中添加spring-cloud-starter-netflix-eureka-client依賴,然后在spring boot啟動類上添加@EnableDiscoveryClient注解以便啟用服務發現,最后在application.yml中添加eureka client的配置信息】,讓其能夠加入到注冊中心中,如下是改造后的配置文件:(注釋掉的是多種方式配置)--eureka 配置節點為新增

server: port: 8866 spring: application: name: configserver profiles: active: native #設置使用本地配置(默認是git,可以設置:subversion(SVN),native(本地)) cloud: config: server: #如下是GIT配置 # git: # uri: https://github.com/zuowj/learning-demos # 配置git倉庫的地址(最后不需要帶/,否則會出現:No custom http config found for URL: XXX) # search-paths: config # git倉庫地址下的相對搜索地址(可用使用通配符),可以配置多個,用,分割。可以{application}實現按應用查配置 # username: # git倉庫的賬號(公開倉庫無需賬號信息) # password: # git倉庫的密碼(公開倉庫無需賬號信息) # default-label: master #git默認分支 #如下是SVN配置 # svn: # uri: http://svnhost:port/svn/app-config #SVN倉庫地址 # username: svnuser #SVN賬號(如果沒有權限可為空) # password: svnpassword#SVN密碼(如果沒有權限可為空) # default-label: trunk #默認SVN分支 #如下是本地文件配置 native: search-locations: classpath:/configs #配置文件存放的目錄 eureka: client: serviceUrl: defaultZone: http://localhost:8800/eureka/
2.5.2.改造服務消費客戶端(config client),集成eureka client(同2.5.1的服務配置中心(config server),集成eureka client步驟),讓其能夠加入到注冊中心中,以便可以通過注冊中心根據服務配置中心(config server)的服務ID(應用名稱)找到config server IP,這里的eureka相關配置應統一在bootstrap.yml中,完整配置如下:
server: port: 8008 spring: application: name: configclient cloud: config: name: configclient #對應config server Url中的{application} profile: prod #配置環境,對應config server Url中的{profile} #label: trunk #配置分支(不配置則默認:git則是master,svn則是trunk), #uri: http://localhost:8866 #配置中心地址 discovery: enabled: true #啟用服務發現 service-id: configserver #指定要從eureka獲取的config server的服務ID(即:configserverr的applicationName) eureka: client: serviceUrl: defaultZone: http://localhost:8800/eureka/
如上配置,重點是spring.cloud.config.uri不再指定固定的config server IP,而是改成配置spring.cloud.config.discovery【服務發現】,eureka配置節點與所有eureka client基本相同不再重述。
都改造完成后,信次先后啟動運行:服務配置中心(config server)【可以運多個實例以不同的端口號】、服務消費客戶端(config client),發現一切都正常,當服務配置中心(config server)某個實例斷開仍不影響服務消費客戶端(config client),這樣就實現了高可用了。
好了,有關spring cloud config 總結就寫到這里了,很多細節若需深究的話還有很多,后面項目中有實際用到再補充總結吧。文中若有不足,歡迎指出,碼字不易,請多支持,謝謝!
分享可參考鏈接:
https://springcloud.cc/spring-cloud-config.html
https://blog.csdn.net/qq_20597727/article/details/82465069
提示:本文相關示例項目代碼均已上傳到GITHUB,地址如下:
https://github.com/zuowj/learning-demos/tree/master/java/demo-configserver
https://github.com/zuowj/learning-demos/tree/master/java/demo-configclient