目錄
在前面有提到,微服務系統是一種典型的分布式系統,我們會將每個功能都盡可能地拆分一個可獨立部署、運行的服務,服務部署完成后,每一次請求的完成,都可能涉及到多個服務的協調作業,面對越來越多的微服務,我們需要有一個東西可以方便地管理配置文件、最好可以在一個地方管理所有微服務的配置,這個就是我們接下來要說的分布式配置組件了。在接下來的內容中,簡稱這個分布式配置組件為配置中心。
4.1 初識分布式配置
在開始介紹Spring Cloud的配置中心實現之前,我們可以先回憶下以前我們的系統配置是怎么做的。
最開始學習的時候,很多東西都是沒有配置的,基本上都是硬編碼,簡單、快速,系統很快構建起來了,可是隨着系統的功能越來越復雜,代碼量越來越大,很多地方進行硬編碼已經不能滿足我們的需求,我們需要在不同的環境、不同的場景下通過修改配置項來達到不一樣的效果,配置項放在代碼里面難找,而且修改起來會很麻煩。
於是,我們將可能頻繁變動的項目提出來放在了xml、properties文件中,方便修改,這個時候修改配置的時候已經不需要再去碰業務代碼,只要找到對應的配置文件,修改、打包、重新部署就可以了。這樣子看起來已經不錯了,但是隨着分布式架構逐漸流行,一個一個的集群被搭建起來,這個時候再給每個集群實例搭配一個配置文件,維護的工作量就變得很大了。
這種情況運維人員一般都有一個config list,記錄了每個實例的配置文件放在哪里、叫什么名字、現在的配置項是什么等等。這個清單如果維護出錯,對整個系統來說都會是一個災難。基於這種情況,分布式系統需要一個中心化的配置中心來替代這個config list,並且可以方便地從不同維度來進行配置,最好支持配置項的隨時,並且最好能夠實時生效。
4.2 Spring Cloud Config
Spring Cloud作為一個完善的微服務框架,提供了Spring Cloud Config作為推薦的分布式配置組件。
Spring Cloud Config提供了服務端和客戶端,可以很好地支持分布式系統的配置需求。使用服務端可以快速構建一個獨立於業務服務之外的配置中心,配置存儲默認使用git,因此它擁有git所擁有的一切優點。同時,它還原生支持使用Spring的Environment和PropertySource來獲取配置項。接下來,我們將使用Spring Cloud Config構建一個配置中心。
4.2.1 Config Server
新建Spring Boot工程,引入Spring Cloud Config的服務端maven依賴,為了方便測試,我們同時引入了web和actuator的相關依賴。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
pom文件編寫完成后,在application.properties中添加配置信息如下:
spring.application.name=config-server
server.port=8888
# 配置git倉庫地址
# 這個地址是本書代碼所在git倉庫地址,這里用到了倉庫中的book-config-repo目錄
spring.cloud.config.server.git.uri=https://github.com/hanbin/book
# 配置倉庫路徑
spring.cloud.config.server.git.search-paths=book-config-repo
# 去掉git權限校驗
spring.cloud.config.server.git.skip-ssl-validation=true
# 制定遠程倉庫中配置文件的customer name
spring.config.name=customer
最后,在啟動類中添加@EnableConfigServer注解開啟Spring Cloud Config的服務端功能,然后啟動即可。
我們在book-config-repo目錄下新建了配置文件:customer-dev.properties文件。我們可以通過customer-server服務提供的接口來訪問這個配置文件。
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
舉例來說,customer-dev.properties中的就是{application}-{profile}.properties的形式,customer即是應用名,profile通常用來標示不同的環境,label在使用git存儲時代表的是git的分支,不寫則默認為master。
上面這些URI都可以獲取到遠程配置,可以看到使用上面格式的地址訪問可以看到如下內容:
http://localhost:8888/customer/dev
http://localhost:8888/customer/dev/master
通過上面兩個鏈接可以得到下面的結果:
{
"name": "customer",
"profiles": [
"dev"
],
"label": "master",
"version": "97ffd82376b9ba87c5a1ffcf60b67eb4866f2415",
"state": null,
"propertySources": [
{
"name": "https://github.com/hanbin/book/book-config-repo/customer-dev.properties",
"source": {
"name": "icer123",
"age": "1001"
}
}
]
}
通過其他格式接口可以直接獲取到的結果都是:
age: 1001
name: icer123
age和name就是我們需要的配置項。
構建完成配置中心后,接下來我們看看如何將微服務構建成為“配置中心客戶端”,來獲取配置中心的配置項。
4.2.2 Config Client
前面我們創建了customer服務,這節我們會將customer服務作為config-server的client,通過遠程獲取的方式拿到customer-dev.properties中的配置項。
在開始之前,這里要提到一個新的endpoint:env。env端點主要用來顯示服務的配置信息。在Spring
Boot2中,env端點默認是關閉的。通過添加下面的代碼到application.properties中,可以讓env端點能夠被訪問。
management.endpoints.web.exposure.include=health,info,env
在瀏覽器中可以看到如圖4.1所示的頁面。

為customer工程添加Spring Cloud Config的支持,需要在pom.xml文件中添加下面的代碼。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
啟動項目,可以在啟動日志中看到如圖4.2所示的內容。

可以看到,添加了Spring Cloud Config的依賴后,項目在啟動過程中會默認向http://localhost:8888請求遠程配置,附帶的參數name為項目的spring.application.name,profiles為default,label為null。
這里的默認配置不能拿到任何信息,因為profiles傳的不對,在customer工程的application.properties配置文件中添加。
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.name=customer
spring.cloud.config.profile=dev
重新啟動程序,可以看到如圖4.3所示的啟動日志。

通過圖4.4可以看到現在customer在啟動時讀取到了之前我們在config server中的配置文件customer-dev.properties。通過本節最開始打開的env端點可以查看,如圖4.4所示。

這里有兩個配置文件需要注意下:spring.application.name 和 spring.cloud.config.name。
spring.application.name一般配置的是微服務的實例名,這個名字是微服務之間調用時候的唯一名稱。spring.cloud.config.name配置的是Spring Cloud Config中涉及的application名稱,在本節的例子中,對應的就是customer。如果去掉spring.cloud.config.name這個配置項,Spring Cloud Config會使用spring.application.name的配置去配置中心取值。
注意:充分使用label,profile可以完美達到一套代碼部署在多個環境,僅通過在啟動參數中修改label和profile就將各個環境進行區分的目的。
4.2.3 配置刷新
前面講的都是在系統啟動之前添加配置,那么在系統運行過程中,能否“熱刷新”,讓配置項在運行期可以被直接改動。
Spring Cloud Config是支持“熱刷新”的,這里需要用到refresh端點。
還是以customer工程為例。為了方便測試,我們將application.properties中的:
management.endpoints.web.exposure.include=health,info,env
換成:
management.endpoints.web.exposure.include=*
這樣,customer的所有端口就已經全部被放開了。
我們新建UserController.java文件,文件內容如下:
package cn.com.hanbinit.customer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RefreshScope
@RestController
public class UserController {
@Value("${name}")
private String name;
@Value("${age}")
private Long age;
@GetMapping("/showinfo")
public String showNameAndAge(){
return "name: " + name + ", age: " + age;
}
}
上面代碼中@RefreshScope注解是實現變量熱加載的必需配置。@Value可以獲取到配置文件以及環境變量中的配置。上面代碼中的name和age就分別對應了配置文件中的name和age。showinfo接口返回了@Value獲取到的屬性值。
啟動項目,訪問http://localhost:8001/showinfo接口,可以看到如圖4.5的返回。

接下來,我們將customer-dev.properties
中的icer改為icer123,100改為1001,push到遠程倉庫。通過postman工具調用refresh端點,如圖4.6所示。

這個時候,刷新showinfo的調用頁面,可以看到如圖4.7的結果。

這里,就說明我們的熱加載已經成功。
注意:這種刷新方式僅適用於非初始化對象的情況下,如果配置項需要在系統啟動的時候加載並且使用。那么在這里使用refresh端點是不會生效的。配置項即使刷新過去,但是確沒有能真正生效。
截止到這里,我們已經可以通過Spring Cloud Config搭建遠程的配置中心,並且可以讓微服務獲取到配置中心的配置項,且可以通過refresh端點達到熱加載的目的。那么能不能實現自動刷新呢?手動調用refresh端點還是比較麻煩的。自動刷新當然是可以的,自動刷新后面有機會再寫。
4.3 小結
本章我們使用Spring Cloud Config創建了配置中心,並對之前的customer微服務進行了改造,讓customer服務可以遠程獲取到配置中心的配置。