本文是Spring Cloud專欄的第十一篇文章,了解前十篇文章內容有助於更好的理解本文:
一、前言
為了保證配置中心服務的高可用,將配置中心服務端做成一個微服務,將其集群化,從而達到高可用,當你部署了多個Config Server實例並預期一個或多個實例不時不可用時,為確保高可用性,你可以指定多個URL(作為spring.cloud.config.uri屬性下的逗號分隔列表),也可以讓所有實例在Eureka等Service Registry中注冊(如果使用發現優先Bootstrap模式)。注意:只有在Config Server未運行時(即應用程序已退出時)或發生連接超時時,才能確保高可用性,例如:如果Config Server返回500(內部服務器錯誤)響應或Config Client從Config Server收到401(由於憑據錯誤或其他原因),則Config Client不會嘗試從其他URL獲取屬性,這種錯誤表示用戶問題而不是可用性問題。
上一篇文章《Spring Cloud第十篇 | 分布式配置中心Config》講述了一個服務如何從配置中心讀取文件,上一篇案例只是通過client直接訪問server,地址直接寫死,這種方式顯然不夠靈活,當服務實例很多時,都從配置中心讀取文件,如果配置中心服務端地址變動的話,那我們所有的服務都啟動不起來了,都需要變更配置中心服務端地址,特別麻煩,由於微服務使用了注冊中心,以及注冊中心的優點等,我們可以結合Eureka注冊中心,將配置中心做成微服務,將其集群化部署,達到配置中心的高可用,架構圖如下:
二、改造配置中心服務端
1、添加注冊中心客戶端依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、主啟動類上添加注解@EnableEurekaClient
3、在application.yml文件中添加Eureka相關配置
eureka:
client:
service-url:
defaultZone: http://localhost:8700/eureka
#客戶端每隔30秒從Eureka服務上更新一次服務信息
registry-fetch-interval-seconds: 30
#需要將我的服務注冊到eureka上
register-with-eureka: true
#需要檢索服務
fetch-registry: true
#心跳檢測檢測與續約時間
instance:
#告訴服務端,如果我10s之內沒有給你發心跳,就代表我故障了,將我剔除掉,默認90s
#Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
lease-expiration-duration-in-seconds: 10
#每隔2s向服務端發送一次心跳,證明自已依然活着,默認30s
#Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
lease-renewal-interval-in-seconds: 2
# 啟用ip配置 這樣在注冊中心列表中看見的是以ip+端口呈現的
prefer-ip-address: true
# 實例名稱 最后呈現地址:ip:2002
instance-id: ${spring.cloud.client.ip-address}:${server.port}
4、然后啟動config server服務端就行了
三、改造配置中心客戶端
1、改造步驟同1-1,1-2,1-3,然后在application.yml文件中將spring.cloud.config配置修改為如下樣子
spring:
cloud:
config:
#uri則表示配置中心的地址
#uri: http://localhost:8888
#注:config 客戶端在沒有 spring.cloud.config.name屬性的時候,服務端{application} 獲取的是客戶端
#spring.application.name的值,否則,獲取的是 spring.cloud.config.name的值。
#1)、當沒有spring.cloud.config.name時,客戶端獲取的是spring.application.name 所對應的git庫中的文件,並且只能獲取一個文件
#2)、當一個項目中有需求要獲取多個文件時,就需要用到spring.cloud.config.name這個屬性,以逗號分割
name: configclient
profile: dev
#label對應了label部分
label: master
discovery:
#表示開啟通過服務名來訪問config-server
enabled: true
#則表示config-server的服務名
service-id: tmgsp-config-server
2、查看注冊中心上注冊的服務
3、然后訪問http://localhost:8881/index可以看到相同效果,如果需要配置中心高可用,只需要將配置中心啟動多個實例就行了。
四、失敗快速響應與重試
1、失敗快速響應
Spring Cloud Config的客戶端會預先加載很多其他信息,然后再開始連接Config Server進行屬性的注入。不作任何額外配置的情況下,失敗響應有點遲鈍,舉個簡單的例子,關掉config-server,我們直接啟動config-client,此時啟動會報錯,但是報錯時間較晚,當我們構建的應用較為復雜的時候,可能在連接Config Server之前花費較長的啟動時間,而在一些特殊場景下,我們又希望可以快速知道當前應用是否能順利地從Config Server獲取到配置信息,這對在初期構建調試環境時,可以減少很多等待啟動的時間。要實現客戶端優先判斷Config Server獲取是否正常,並快速響應失敗內容,只需在Config客戶端的bootstrap.yml中配置參數spring.cloud.config.failFast=true即可。
此時不啟動config-server直接啟動config-client依然會報錯,但是我們看到報錯時間較早,系統都沒打印幾條啟動日志。
2、重試
上面我們提過了當Config Server宕機或是客戶端配置不正確導致連接不到Config Server而啟動失敗的情況,快速響應的配置可以發揮比較好的效果。但是,如果網絡波動等其他間歇性原因導致的問題,直接啟動失敗似乎代價有些高。所以Config客戶端還提供了自動重試的功能,在開啟重試功能前,先確保已經配置了(確保開啟失敗快速響應)spring.cloud.config.failFast=true在進行下面操作。
2-1、在客戶端添加依賴
<!--連接config-server重試機制相關依賴--> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2-2、不需要再做其他任何配置,啟動客戶端應用,在控制台中可以看到如下內容。客戶端在連接Config Server失敗之后,會繼續嘗試,直到第6次失敗后,才返回錯誤信息。通過這樣的重試機制,可以避免一些間歇性問題引起的失敗導致客戶端應用無法啟動的情況。查看日志如下,測試的時候切記Config Server需要在注冊中心沒有剔除掉,要是剔除掉那就沒的說了。
INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881 - Re-registering apps/SPRINGCLOUD-CONFIG-CLIENT INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881: registering service... INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881 - registration status: 204 INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available ERROR 9996 --- [ main] o.s.boot.SpringApplication : Application run failed
2-3、從日志上可以看出Config client一共嘗試了六次去訪問Config server,六次都失敗了才拋異常和重試機制相關的配置有如下四個:
# 配置重試次數,默認為6 spring.cloud.config.retry.max-attempts=6 # 間隔乘數,默認1.1 spring.cloud.config.retry.multiplier=1.1 # 初始重試間隔時間,默認1000ms spring.cloud.config.retry.initial-interval=1000 # 最大間隔時間,默認2000ms spring.cloud.config.retry.max-interval=2000
五、動態刷新配置
1、有的時候,我動態的更新了Git倉庫中的配置文件,那么我如何讓我的config-client能夠及時感知到呢?方式很簡單,首先在config-client中添加如下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
由於我們父模塊(springcloud-learn)依賴中已經引入該依賴,所以可以省略不引入。
該依賴中包含了/refresh端點的實現,我們將利用這個端點來刷新配置信息。
2、在需要刷新的Bean上添加@RefreshScope注解,不然客戶端不知道刷新哪里
3、然后需要在application.yml中配置暴露/refresh端點
management:
endpoints:
web:
exposure:
include: ["info","health","refresh"]
4、然后啟動訪問http://localhost:8881/index
5、修改碼雲上的configclient-dev.yml文件,因為我們客戶端加載的是這個配置文件
6、然后用postman工具向http://localhost:8881/actuator/refresh地址發送POST請求(該端點只接受post請求,日志上可以看出),結果如下:
7、重新訪問http://localhost:8881/index顯示內容如下
詳細參考案例源碼:https://gitee.com/coding-farmer/springcloud-learn