對於一個微服務架構的應用來說,每一個服務都有自己的一個組態檔,當微服務的數量比較多時,如果修改一些公用的設定,則需要將所有的組態檔都單獨進行修改,這對運維人員來說是非常耗時耗力的。那麼有沒有一種方法,可以將衆多微服務中公用的設定資訊抽取出來,放到一個地方集中管理,然後各個微服務再去這個地方拉取對應的設定資訊,達到公用設定統一管理的目的。所以設定中心就出現了。
網際網路行業常用的設定中心有SpringCloud Config、Alibaba Nacos、攜程的Apollo等,這裏只說明Config的使用方式。
SpringCloud Config採用C/S架構,爲微服務架構中的微服務提供集中化的外部設定支援,分爲伺服器端和用戶端。官方文件:
https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.1.RELEASE/reference/html/
伺服器端:也稱分佈式設定中心,它是一個獨立的微服務應用,用來連線設定伺服器併爲用戶端提供獲取設定資訊的存取介面。設定伺服器預設採用git來儲存設定資訊,這樣有助於對環境設定進行版本控制,並且可以通過git用戶端工具來方便的管理和存取設定內容。而Config Server只需要和git設定伺服器連線就行了。
用戶端:需要獲取設定的各個微服務,在啓動的時候從伺服器端獲取指定的設定資訊。
因爲Config Server預設採用git來儲存設定資訊,所以此處選擇github作爲儲存伺服器,並與之整合。
第一步:環境準備
首先,在github上新建springcloud-config倉庫並建立如下檔案:
注意:一般情況下,我們都是使用application-profile.yml/properties的方式命名組態檔!
組態檔中書寫一下測試內容:
#dev環境
config:
info: "master branch,springcloud-config/appname-dev.yml version=1"
#測試環境
config:
info: "master branch,springcloud-config/appname-test.yml version=1"
#生產環境
config:
info: "master branch,springcloud-config/appname-prod.yml version=1"
然後,從github上下載剛纔建立好的倉庫,命令(SSH方式):
git clone [email protected]:XXXXXX/springcloud-config.git
得到下列目錄:
第二步:建立Config Server模組
pom:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
application.yml:
server:
port: 3344
spring:
application:
name: cloud-config-server
cloud:
config:
server:
git:
#設定第一步中建立的倉庫地址(http方式)
uri: https://github.com/dengbuquan/springcloud-config.git
#搜尋目錄
search-paths:
- springcloud-config
#讀取分支
label: master
#註冊進Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
主啓動類:
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigServerMain3344.class,args);
}
}
第三步:啓動Config Server模組,在瀏覽器中輸入http://localhost:3344/master/appname-dev.yml,得到:
config:
info: master branch,springcloud-config/appname-dev.yml version=1
更換請求地址:http://localhost:3344/master/appname-test.yml,得到:
config:
info: master branch,springcloud-config/appname-test.yml version=1
測試成功!!!,此時Config Server已經和線上的設定伺服器連通,可以正常獲得線上倉庫中的組態檔資訊了。
下一步我們要做的就是在Config Client中連通Config Server,將Config Server從線上獲得的資訊整合成Client服務的組態檔。
注意:在第三步中,請求的路徑是有規定的,可供使用的有一下五種:
但是比較推薦的還是第三種,可以指明分支、檔名稱以及環境資訊。
第一步:新建Config Client模組
pom:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
bootstrap.yml:
server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
#代表從http://localhost:3344/master/appname-dev.yml(properties)獲取設定資訊
uri: http://localhost:3344
label: master
name: appname
profile: dev
#註冊進Eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
bootstrap.yml和application.yml:
因爲我們要從設定中心獲取通用的設定資訊,在SpringCloud中,預設採用bootstrap作爲雲組態檔的名稱,它的載入優先順序比application.yml高,兩個組態檔共同生效,application.yml中通常設定一些定製化的設定資訊,當兩個檔案中的內容有衝突時,以bootstrap中的爲準。
bootstrap的圖示和application.yml不一樣,是一朵雲,代表「雲設定」。
主啓動類:
@SpringBootApplication
@EnableEurekaClient
//此處不需要開啓任何Config的功能支援
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class,args);
}
}
測試用業務類:
@RestController
public class ConfigController {
//將組態檔中的config.info系結給configInfo變數
//因爲bootstrap.yml中沒有顯式定義config.info資訊,如果系結成功,代表雲獲取成功
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
第二步:啓動3355模組,存取 http://localhost:3355/configInfo ,得到:
master branch,springcloud-config/appname-dev.yml version=1
測試成功,此時代表着,遠端倉庫中的設定資訊已經被應用到了Config Client中了。
在實際開發中,組態檔中值是用來初始化bean的,只有在初始化的時候才能 纔能夠把組態檔中的值應用到各個元件中。但是有一些值在執行過程中可能會發生變化,這時要在不重新啓動服務的前提下使得最新的設定生效,就用到了設定的動態重新整理功能。
首先,我們要知道Config Client→→Config Server→→github的同步時機:
Config Server本身是不儲存github中組態檔的資訊的,Config Server每一次收到外界關於設定資訊的請求時,都會即時的向github發送獲取資訊的請求,所以Config Server每一次獲取到的設定資訊都是最新的。但是,Config Client只會在啓動的時候纔會向Config Server發送獲取設定的請求,然後利用這些資訊初始化bean。如果在Config Client執行期間,遠端的設定資訊發生了變化,因爲所有的bean已經初始化完成,那麼Config Client是沒有辦法及時更新自己的設定資訊的。如果想要使用最新的設定,必須要重新啓動Config Client,觸發bean的初始化。但是當Config Client比較多,或者啓動比較耗時時,重新啓動Config Client顯然是不可取的。
爲了解決Config Client無法動態重新整理設定的問題,Config給我們提供一套解決方案,可以幫助Config Client快速的應用最新設定,而不用重新啓動。
第一步:修改3355模組
pom:新增actuator監控
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yml:新增actuator設定,bootstrap.yml和application.yml都行
management:
endpoints:
web:
exposure:
include: "*"
第二步:預估程式執行期間需要重新初始化的bean,在@Controller類上或者注入bean的方法上使用@RefreshScope註解,表示此bean可以在程式執行時重新初始化,並重新裝配所有依賴它的元件。
注意:這一步關鍵在於要預估那些bean需要在程式執行期間重新初始化,對程式碼有一定的侵入性。對於那些沒有標註此註解的bean來講,想要應用最新的設定資訊,就只能重新啓動服務了。
@RestController
//此控制器可以被動態的重新整理,在執行期間重新系結組態檔中的值
@RefreshScope
public class ConfigController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
第三步:發送post請求,重新整理3355(這裏使用curl)。相當於通知actuator拉取最新的設定並執行重新整理操作,重新初始化@RefreshScope的Bean並重新裝配依賴它們的元件。
curl -X POST "http://localhost:3355/actuator/refresh"
當需要重新整理的用戶端比較多時,可以編寫一個指令碼檔案,批次執行重新整理操作。
在Spring Cloud Config設定當中,如果我們需要讓ConfigClient使用最新的設定,必須要手動的向每一個ConfigClient發送POST請求,雖然我們可以通過書寫批次檔來應對用戶端較多的情況,但是歸根節點還是對每一個用戶端都發送了POST請求。
那麼有沒有一種方式,可以讓我們只通知一個節點,然後讓所有的節點自動的執行重新整理呢?
Spring Cloud Bus就是這樣的一門技術,它可以將分佈式系統的微服務與訊息中介軟體連線到一起,利用訊息中介軟體完成訊息的擴散,類似於廣播技術。將Spring Cloud Bus和Spring Cloud Config配合使用,我們就可以達到通知一個節點,所有節點自動重新整理的效果。
什麼是訊息匯流排:
在微服務系統中,通常會利用一些輕量級的訊息中介軟體來構建一個公用的訊息主題,並讓系統中的所有微服務範例都連線到此主題,由於該主題產生的訊息會被所有的範例監聽和消費,所以稱它爲訊息匯流排。可以通過總線上的任何一個範例方便地廣播一些需要其他範例都知道的訊息。
基本原理:
將Spring Cloud Bus安裝到每一個微服務當中,並進行相關設定,微服務啓動之後,在MQ伺服器中就會建立一個預設爲springCloudBus的topic,對於此topic來講,每一個微服務範例既是生產者也持久訂閱者,可以發佈訊息,也可以接收訊息。
目前爲止,Bus僅支援RabbitMQ和Kafka兩種訊息中介軟體,本次整合演示使用RabbitMQ。
第一步:安裝Erlang
http://erlang.org/download/otp_win64_21.3.exe
第二步:安裝RabbitMQ
https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
第三步:設定RabbitMQ視覺化外掛
使用DOS進入RabbitMQ的安裝目錄下的sbin目錄,例如:D:\RabbitMQ\rabbitmq_server-3.7.14\sbin,
然後執行命令:rabbitmq-plugins enable rabbitmq_management
然後就可以在選單中看到RabbitMQ的快捷操作外掛了,可以方便我們啓動或者關閉RabbitMQ。
第四步:存取RabbitMQ的控制檯,http://localhost:15672,如果得到登錄頁面,代表RabbitMQ安裝和啓動成功。(安裝成功後自動啓動)
第五步:登錄RabbitMQ,預設的賬號密碼均爲guest,登錄成功之後就可以進入RabbitMQ的主頁面。
環境搭建:
在Spring Cloud Config搭建的案例基礎上,拷貝cloud-config-client3355,新建一個cloud-config-client3366,這樣就有了一個設定中心cloud-config-server3344,兩個用戶端cloud-config-client3355、cloud-config-client3366。接下來完成Bus和Config的整合:
第一步:啓動RabbitMQ,Bus依賴RabbitMQ,必須優先啓動它
第二步:修改設定中心cloud-config-server3344
pom:
<!--增加下面 下麪的依賴-->
<!--bus依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--監控依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yml:
#增加下面 下麪的設定
spring:
#設定RabbitMQ伺服器地址和使用者名稱密碼
rabbitmq:
host: localhost
#RabbitMQ預設的JMS伺服器端口
port: 5672
username: guest
password: guest
#actuator設定
management:
endpoints:
web:
exposure:
include: "bus-refresh"
第三步:用戶端cloud-config-client3355和cloud-config-client3366
pom:
<!--增加下面 下麪的依賴-->
<!--bus依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--監控依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yml:
#增加下面 下麪的設定
spring:
#設定RabbitMQ伺服器地址和使用者名稱密碼
rabbitmq:
host: localhost
#RabbitMQ預設的JMS伺服器端口
port: 5672
username: guest
password: guest
#actuator設定
management:
endpoints:
web:
exposure:
#這裏使用*,表示暴露所有端點,既能夠實現手動重新整理,又能夠實現自動重新整理
include: "*"
第四步:依次啓動EurekaServer、設定中心cloud-config-server3344,兩個用戶端cloud-config-client3355、cloud-config-client3366。
第五步:存取RabbitMQ的控制檯,http://localhost:15672,並登錄,在Exchanges索引標签下可以看到有一個名爲springCloudBus的topic產生了。
此時代表Bus整合成功,各個微服務已經與RabbitMQ連線到了一起,並共同訂閱springCloudBus主題,並且可以向此主題發送訊息。
第六步:完成上述幾步後,就可以開始進行測試了,在github上更新組態檔,然後存取: http://localhost:3344/master/appname-dev.yml ,此時設定中心的設定會立刻得到更新,接着存取 http://127.0.0.1:3355/configInfo 和 http://127.0.0.1:3366/configInfo (用戶端設定的測試介面),發現訊息沒有得到更新。
這是因爲我們還沒有重新整理設定。接下來使用Bus爲我們提供的自動重新整理介面,向任意一個微服務範例發送post請求:
//bus-refresh是Bus爲我們提供的自動重新整理的介面,不可自定義,並且要在微服務設定中暴露此端點
//向設定中心發送請求
curl -X POST "http://localhost:3344/actuator/bus-refresh"
//或者向用戶端發送請求:
curl -X POST "http://localhost:3355/actuator/bus-refresh"
curl -X POST "http://localhost:3366/actuator/bus-refresh"
發送成功之後再次請求 http://127.0.0.1:3355/configInfo 和 http://127.0.0.1:3366/configInfo,就可以得到最新的設定了,和github同步。
注意:因爲每一個微服務範例都是springCloudBus主題的訂閱者和生產者,所以我們對哪一個微服務範例發送post請求都是可以的,只要他們暴露了bus-refresh端點。但是爲了保證單一職能原則,我們一般不向業務模組發送post請求,而是想設定中心發送。
通過上面的設定,我們已經完成了Config和Bus的整合,實現了一次post請求完成全部的重新整理,是不是很方便,但是目前還只能進行全部的重新整理,如果我們只想更新部分的微服務怎麼辦呢?
Bus給我們提供了定點廣播的方式,可以在發送post請求時指定具體的微服務範例,來控制哪些微服務重新整理,哪些不重新整理。如下:
//僅重新整理config-client:3355
//destination=服務名:埠號
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
此時再次更新github上的組態檔,使用上面的方式執行重新整理,然後依次存取 http://127.0.0.1:3355/configInfo 和 http://127.0.0.1:3366/configInfo,可以看到只有config-client:3355得到了更新,config-client:3366未更新。