目錄
1. SpringCloud Hystrix(服務熔斷與降級組件 / 服務容錯與保護組件)
2. SpringCloud Gateway(網(wǎng)關組件)
3. SpringCloud Config(分布式配置組件)
1. SpringCloud Hystrix(服務熔斷與降級組件 / 服務容錯與保護組件)

例:服務依賴關系
在微服務架構中,一個應用往往由多個服務組成,這些服務之間相互依賴,依賴關系錯綜復雜。
通常情況下,一個用戶請求往往需要多個服務配合才能完成。例如上圖所示,在所有服務都處于可用狀態(tài)時,請求 1 需要調用 A、D、E、F 四個服務才能完成,請求 2 需要調用 B、E、D 三個服務才能完成,請求 3 需要調用服務 C、F、E、D 四個服務才能完成。
當服務 E 發(fā)生故障或網(wǎng)絡延遲時,會出現(xiàn)以下情況:
1. 即使其他所有服務都可用,由于服務 E 的不可用,那么用戶請求 1、2、3 都會處于阻塞狀態(tài),等待服務 E 的響應。在高并發(fā)的場景下,會導致整個服務器的線程資源在短時間內(nèi)迅速消耗殆盡。
2. 所有依賴于服務 E 的其他服務,例如服務 B、D 以及 F 也都會處于線程阻塞狀態(tài),等待服務 E 的響應,導致這些服務的不可用。
3. 所有依賴服務B、D 和 F 的服務,例如服務 A 和服務 C 也會處于線程阻塞狀態(tài),以等待服務 D 和服務 F 的響應,導致服務 A 和服務 C 也不可用。
當微服務系統(tǒng)的一個服務出現(xiàn)故障時,故障會沿著服務的調用鏈路在系統(tǒng)中瘋狂蔓延,最終導致整個微服務系統(tǒng)的癱瘓,這就是【雪崩效應】。為了防止此類事件的發(fā)生,微服務架構引入了“熔斷器”的一系列服務容錯和保護機制。
熔斷器(Circuit Breaker)
與物理學中的熔斷器作用(當線路出現(xiàn)故障時,迅速切斷電源以保護電路的安全)相似,微服務架構中的熔斷器能夠在某個服務發(fā)生故障后,向服務調用方返回一個符合預期的、可處理的降級響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常。這樣就保證了服務調用方的線程不會被長時間、不必要地占用,避免故障在微服務系統(tǒng)中的蔓延,防止系統(tǒng)雪崩效應的發(fā)生。
Hystrix(服務容錯與保護組件)讓服務擁有自我保護的能力
1. 保護線程資源
防止單個服務的故障耗盡系統(tǒng)中的所有線程資源。
2. 快速失敗機制
當某個服務發(fā)生了故障,不讓服務調用方一直等待,而是直接返回請求失敗。
3. 監(jiān)控功能
提供熔斷器故障監(jiān)控組件HystrixDashboard(隨時監(jiān)控熔斷器的狀態(tài))。Hystrix 會持續(xù)地記錄所有通過 Hystrix 發(fā)起的請求的執(zhí)行信息,并以統(tǒng)計報表的形式展示給用戶,包括每秒執(zhí)行請求的數(shù)量、成功請求的數(shù)量和失敗請求的數(shù)量等。
4. 服務降級 FallBack(保證當前服務不受其他服務故障的影響,提高服務的健壯性)
(既可以在服務端,也可以在客戶端)提供一個請求失敗后的降級方案(通常是一個兜底方法,當請求失敗后即調用該方法)。
使用場景:
1. 在服務器壓力劇增時,根據(jù)實際業(yè)務情況及流量,對一些不重要、不緊急的服務進行有策略地不處理或簡單處理,從而釋放服務器資源以保證核心服務正常運作。
2. 當某些服務不可用時,為了避免長時間等待造成服務卡頓或雪崩效應,而主動執(zhí)行備用的降級邏輯立刻返回一個友好的提示,以保障主體業(yè)務不受影響。
3. 程序運行異常、服務超時、熔斷器處于打開狀態(tài)、線程池資源耗盡。
使用步驟:
重寫HystrixCommand的getFallBack()方法 或 HystrixObservableCommand的resumeWithFallback()方法來使服務支持降級。
5. 服務熔斷(防止故障擴散到其他服務)
為了應對雪崩效應而出現(xiàn)的一種微服務鏈路保護機制。
Hystrix會監(jiān)控微服務間調用的狀況,當某個微服務不可用 或 響應時間太長 或 失敗調用到一定比例時,為了保護系統(tǒng)的整體可用性,就會啟動熔斷機制(暫時切斷請求對該服務的調用,并快速返回一個友好的錯誤響應)。這種熔斷狀態(tài)不是永久的,在經(jīng)歷了一定的時間后,熔斷器會再次檢測該微服務是否恢復正常,若服務恢復正常則恢復其調用鏈路。
熔斷狀態(tài)(3種):
1. 熔斷關閉狀態(tài)(Closed)
當服務訪問正常時,熔斷器處于關閉狀態(tài),服務調用方可以正常地對服務進行調用。
2. 熔斷開啟狀態(tài)(Open)
默認情況下,在固定時間內(nèi)接口調用出錯比率達到一個閾值(例如 50%),熔斷器會進入熔斷開啟狀態(tài)。進入熔斷狀態(tài)后,后續(xù)對該服務的調用都會被切斷,熔斷器會執(zhí)行本地的降級(FallBack)方法。
3. 半熔斷狀態(tài)(Half-Open)
在熔斷開啟一段時間之后,熔斷器會進入半熔斷狀態(tài)。在半熔斷狀態(tài)下,熔斷器會嘗試恢復服務調用方對服務的調用,允許部分請求調用該服務,并監(jiān)控其調用成功率。如果成功率達到預期,則說明服務已恢復正常,熔斷器進入關閉狀態(tài);如果成功率仍舊很低,則重新進入熔斷開啟狀態(tài)。
熔斷流程:
1. 當服務的調用出錯率達到或超過Hystix規(guī)定的比率(默認為50%)后,熔斷器進入熔斷開啟狀態(tài)。
2. 熔斷器進入熔斷開啟狀態(tài)后,Hystrix會啟動一個休眠時間窗,在這個時間窗內(nèi),該服務的降級邏輯會臨時充當業(yè)務主邏輯,而原來的業(yè)務主邏輯不可用。
3. 當有請求再次調用該服務時,會直接調用降級邏輯快速地返回失敗響應,以避免系統(tǒng)雪崩。
4. 當休眠時間窗到期后,Hystrix 會進入半熔斷轉態(tài),允許部分請求對服務原來的主業(yè)務邏輯進行調用,并監(jiān)控其調用成功率。
5. 如果調用成功率達到預期,則說明服務已恢復正常,Hystrix 進入熔斷關閉狀態(tài),服務原來的主業(yè)務邏輯恢復;否則 Hystrix 重新進入熔斷開啟狀態(tài),休眠時間窗口重新計時,繼續(xù)重復第 2 到第 5 步。
SpringCloudHystrix
對Netflix公司的Hystrix開源組件進行二次封裝,提供了熔斷器功能,能夠有效地阻止分布式微服務系統(tǒng)中出現(xiàn)聯(lián)動故障,以提高微服務系統(tǒng)的彈性。
具有服務降級、服務熔斷、線程隔離、請求緩存、請求合并以及實時故障監(jiān)控等強大功能。
@HystrixCommand注解的commandProperties屬性的參數(shù)
1. metrics.rollingStats.timeInMilliseconds(統(tǒng)計時間窗)
監(jiān)控統(tǒng)計時間窗內(nèi)的服務調用出錯率。
2. circuitBreaker.sleepWindowInMilliseconds(休眠時間窗)
熔斷開啟狀態(tài)持續(xù)一段時間后,熔斷器會自動進入半熔斷狀態(tài),這段時間就被稱為休眠窗口期。
3. circuitBreaker.requestVolumeThreshold(請求總數(shù)閥值)
在統(tǒng)計時間窗內(nèi),請求總數(shù)必須到達一定的數(shù)量級,Hystrix 才可能會將熔斷器打開進入熔斷開啟轉態(tài),而這個請求數(shù)量級就是 請求總數(shù)閥值。Hystrix 請求總數(shù)閾值默認為 20,這就意味著在統(tǒng)計時間窗內(nèi),如果服務調用次數(shù)不足 20 次,即使所有的請求都調用出錯,熔斷器也不會打開。
4. circuitBreaker.errorThresholdPercentage(錯誤百分比閾值)
當請求總數(shù)在統(tǒng)計時間窗內(nèi)超過了請求總數(shù)閥值,且請求調用出錯率超過一定的比例,熔斷器才會打開進入熔斷開啟轉態(tài),而這個比例就是錯誤百分比閾值。錯誤百分比閾值設置為 50,就表示錯誤百分比為 50%,如果服務發(fā)生了 30 次調用,其中有 15 次發(fā)生了錯誤,即超過了 50% 的錯誤百分比,這時候將熔斷器就會打開。

三種熔斷狀態(tài)轉換
示例(服務端降級)
以之前的例子為基礎
1. 創(chuàng)建spring-cloud-provider-user-hystrix-8004服務提供者,修改pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--添加 Spring Boot 的監(jiān)控模塊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- eureka 客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--hystrix 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2. 創(chuàng)建application.yml(類路徑resources下)
server:
port: 8004 #服務端口號
spring:
application:
name: springCloudProviderUserHystrix #對外暴露的微服務名稱
jackson:
serialization:
FAIL_ON_EMPTY_BEANS: false
######### SpringCloud自定義服務名稱和ip地址 #############
eureka:
client: #將客戶端注冊到eureka服務列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #該地址為 7001注冊中心在application.yml中暴露出來的注冊地址 (單機版)
instance:
instance-id: spring-cloud-provider-8004 #自定義服務名稱信息
prefer-ip-address: true #顯示訪問路徑的ip地址
########### SpringCloud使用 SpringBootActuator 監(jiān)控完善信息 ########
# SpringBoot2.50對 actuator監(jiān)控屏蔽了大多數(shù)的節(jié)點,只暴露了heath節(jié)點,本段配置(*)就是為了開啟所有的節(jié)點
management:
endpoints:
web:
exposure:
include: "*" # * 在yaml 文件屬于關鍵字,所以需要加引號
info:
app.name: spring-cloud-provider-user-hystrix
company.name: com.sst.cx
build.aetifactId: @project.artifactId@
build.version: @project.version@
3. 創(chuàng)建UserService.java(com.sst.cx.service)
package com.sst.cx.service;
public interface UserService {
// hystrix 熔斷器ok
public String userInfo_Ok(Integer id);
// hystrix 熔斷器超時
public String userInfo_Timeout(Integer id);
}
4. 創(chuàng)建UserServiceImpl.java(com.sst.cx.service.impl)
package com.sst.cx.service.impl;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
import com.sst.cx.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public String userInfo_Ok(Integer id) {
return "線程池:" + Thread.currentThread().getName() + " userInfo_Ok,id: " + id;
}
// 一旦該方法失敗并拋出了異常信息后,會自動調用@HystrixCommand注解標注的fallbackMethod指定的方法(即指定降級方法)
@HystrixCommand(fallbackMethod = "user_TimeoutHandler",
commandProperties =
// 規(guī)定5秒鐘以內(nèi)就不報錯,正常運行,超過5秒就報錯,調用指定的方法
{@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")})
@Override
public String userInfo_Timeout(Integer id) {
int outTime = 6;
try {
TimeUnit.SECONDS.sleep(outTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "線程池:" + Thread.currentThread().getName() + " userInfo_Timeout,id: " + id + " 耗時: " + outTime;
}
// 當服務出現(xiàn)故障后,調用該方法給出友好提示
public String user_TimeoutHandler(Integer id) {
return "系統(tǒng)繁忙請稍后再試!"+"線程池:" + Thread.currentThread().getName() + " userInfo_Timeout,id: " + id;
}
}
5. 創(chuàng)建UserController.java(com.sst.cx.controller)
package com.sst.cx.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import com.sst.cx.service.UserService;
@RestController
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/user/hystrix/ok/{id}")
public String userInfo_Ok(@PathVariable("id") Integer id) {
String result = userService.userInfo_Ok(id);
log.info("端口號:" + serverPort + " result:" + result);
return result + ", 端口號:" + serverPort;
}
// Hystrix服務超時降級
@RequestMapping(value = "/user/hystrix/timeout/{id}")
public String userInfo_Timeout(@PathVariable("id") Integer id) {
String result = userService.userInfo_Timeout(id);
log.info("端口號:" + serverPort + " result:" + result);
return result + ", 端口號:" + serverPort;
}
}
6. 創(chuàng)建主啟動類
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient // 開啟Eureka客戶端功能
@EnableHystrix // 激活熔斷器功能
public class ServiceCloudProviderUser8004Application {
public static void main(String[] args) {
SpringApplication.run(ServiceCloudProviderUser8004Application.class, args);
}
}
7. 依次啟動服務注冊中心集群、spring-cloud-provider-user-hystrix-8004,
在瀏覽器中訪問http://www.eureka7001.com:8004/user/hystrix/ok/1(正常訪問)、http://www.eureka7001.com:8004/user/hystrix/timeout/1(服務降級)
示例(客戶端降級)
對spring-cloud-feign-user-80項目添加Hystrix功能
1. 在pom.xml文件中添加Hystrix依賴
<!--hystrix 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
2. 在application.yml文件中添加
####### 配置所有請求超時時間(單位為毫秒) ##########
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 7000
####### 配置具體請求超時時間 ########
UserHystrixService#userInfo_Timeout(Integer):
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
###### 開啟hystrix
feign:
hystrix:
enabled: true #開啟客戶端hystrix
3. 創(chuàng)建UserHystrixService.java(com.sst.cx.service)
package com.sst.cx.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Component
@FeignClient(value = "springCloudProviderUserHystrix")
public interface UserHystrixService {
@RequestMapping(value = "/user/hystrix/ok/{id}")
public String userInfo_Ok(@PathVariable("id") Integer id);
@RequestMapping(value = "/user/hystrix/timeout/{id}")
public String userInfo_Timeout(@PathVariable("id") Integer id);
}
4. 創(chuàng)建HystrixController_Consumer.java(com.sst.cx.controller)
package com.sst.cx.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import com.sst.cx.service.UserHystrixService;
@Slf4j
@RestController
public class HystrixController_Consumer {
@Resource
private UserHystrixService userHystrixService;
@RequestMapping(value = "/consumer/user/hystrix/ok/{id}")
public String userInfo_Ok(@PathVariable("id") Integer id) {
return userHystrixService.userInfo_Ok(id);
}
// 在客戶端進行降級
@RequestMapping(value = "/consumer/user/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "user_TimeoutHandler") // 為該請求指定專屬的回退方法
public String userInfo_Timeout(@PathVariable("id") Integer id) {
String s = userHystrixService.userInfo_Timeout(id);
log.info(s);
return s;
}
// userInfo_Timeout方法的 專用 fallback 方法
public String user_TimeoutHandler(@PathVariable("id") Integer id) {
log.info("userInfo_Timeout 出錯,服務已被降級!");
return "服務端系統(tǒng)繁忙,請稍后再試?。蛻舳?userInfo_Timeout 專屬的回退方法觸發(fā))";
}
}
5. 在主啟動類上添加@EnableHystrix注解啟用Hystrix。
6. 修改spring-cloud-provider-user-hystrix-8004的UserServiceImpl.java的userInfo_Timeout()方法的outTime改為4(讓客戶端來處理降級)。
7. 重啟spring-cloud-provider-user-hystrix-8004、spring-cloud-feign-user-80
在瀏覽器中訪問http://www.eureka7001.com/consumer/user/hystrix/timeout/1
/*
1. hystrix.command.xxx#yyy(zzz).execution.isolation.thread.timeoutInMilliseconds=mmm
2. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=mmm
說明
xxx:為包含該服務方法的類的名稱(通常為服務綁定接口的名稱),例如 UserHystrixService 接口。
yyy:服務方法名,例如 userInfo_Timeout() 方法。
zzz:方法內(nèi)的參數(shù)類型,例如 Integer、String 等等
mmm:要設置的超時時間,單位為毫秒(1 秒 =1000 毫秒)
*/
【存疑:配置具體請求超時時間無效,正常請求未降級】
示例(全局降級方法)
通過上面的方式實現(xiàn)服務降級時,需要針對所有業(yè)務方法都配置降級方法,這極有可能會造成代碼的急劇膨脹。為了解決該問題,可以為所有業(yè)務方法指定一個全局的回退方法。
全局降級方法的優(yōu)先級較低,只有業(yè)務方法沒有指定其降級方法時,服務降級時才會觸發(fā)全局回退方法。若業(yè)務方法指定它自己的回退方法,那么在服務降級時,就只會直接觸發(fā)它自己的回退方法,而非全局回退方法。
HystrixController_Consumer.java中
1. 給該類添加@DefaultProperties注解,通過其defaultFallback屬性指定一個全局的降級方法。
@DefaultProperties(defaultFallback = "user_Global_FallbackMethod") // 全局的服務降級方法
2. 創(chuàng)建該方法。
public String user_Global_FallbackMethod() {
return "運行出錯或服務端系統(tǒng)繁忙,請稍后再試?。蛻舳巳只赝朔椒ㄓ|發(fā),)";
}
3. 在所有的業(yè)務方法上都標注@HystrixCommand注解。
重啟spring-cloud-feign-user-80,
在瀏覽器中訪問http://www.eureka7001.com/consumer/user/hystrix/timeout/1
【存疑:配置具體請求超時時間無效,正常請求未降級】
示例(解耦降級邏輯)
不管是業(yè)務方法指定的降級方法還是全局降級方法,它們都必須和業(yè)務方法在同一個類中才能生效,業(yè)務邏輯與降級邏輯耦合度極高。
1. 創(chuàng)建UserHystrixFallBackService.java,實現(xiàn)UserHystrixService(com.sst.cx.service)
package com.sst.cx.service;
import org.springframework.stereotype.Component;
@Component
public class UserHystrixFallBackService implements UserHystrixService {
@Override
public String userInfo_Ok(Integer id) {
return "--------------------系統(tǒng)繁忙,請稍后重試?。ń怦罨赝朔椒ㄓ|發(fā))-----------------------";
}
@Override
public String userInfo_Timeout(Integer id) {
return "--------------------系統(tǒng)繁忙,請稍后重試?。ń怦罨赝朔椒ㄓ|發(fā))-----------------------";
}
}
2. 修改UserHystrixService.java的@FeignClient注解,添加fallback屬性為UserHystrixFallBackService.class
@FeignClient(value = "springCloudProviderUserHystrix",fallback = UserHystrixFallBackService.class)
3. 去除HystrixController_Consumer.java的@DefaultProperties注解、降級方法。
4. 重啟spring-cloud-feign-user-80,
在瀏覽器中訪問http://www.eureka7001.com/consumer/user/hystrix/timeout/1
【存疑:配置具體請求超時時間無效,正常請求未降級】
示例(驗證熔斷機制)
1. 在spring-cloud-provider-user-hystrix-8004的UserService接口中,添加一個userCircuitBreaker()方法:
// hystrix 熔斷機制
public String userCircuitBreaker(Integer id);
2. 在UserServiceImpl.java中,添加userCircuitBreaker()方法實現(xiàn):
//
@Override
@HystrixCommand(fallbackMethod = "userCircuitBreaker_fallback", commandProperties = {
// 以下參數(shù)在 HystrixCommandProperties 類中有默認配置
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否開啟熔斷器
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1000"), // 統(tǒng)計時間窗
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 統(tǒng)計時間窗內(nèi)請求次數(shù)
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 休眠時間窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), // 在統(tǒng)計時間窗口期以內(nèi),請求失敗率達到 60%時進入熔斷狀態(tài)
})
public String userCircuitBreaker(Integer id) {
if (id < 0) {
// 當傳入的 id 為負數(shù)時,拋出異常,調用降級方法
throw new RuntimeException("id 不能是負數(shù)!");
}
return Thread.currentThread().getName() + "\t" + "調用成功";
}
public String userCircuitBreaker_fallback(Integer id) {
return "id 不能是負數(shù),請稍后重試!\t id:" + id;
}
3. 在UserController.java中,添加userCircuitBreaker()方法(對外提供服務):
// Hystrix服務熔斷
@RequestMapping(value = "/user/hystrix/circuit/{id}")
public String deptCircuitBreaker(@PathVariable("id") Integer id){
String result = userService.userCircuitBreaker(id);
log.info("result:"+result);
return result;
}
4. 重啟spring-cloud-provider-user-hystrix-8004
在瀏覽器中訪問http://www.eureka7001.com:8004/user/hystrix/circuit/1;
多次請求http://www.eureka7001.com:8004/user/hystrix/circuit/-1,使調用出錯率大于錯誤百分比閥值;
多次請求http://www.eureka7001.com:8004/user/hystrix/circuit/1,當服務調用正確率上升到一定的利率后,Hystrix 進入熔斷關閉狀態(tài)。
【存疑:一直沒進入熔斷狀態(tài)】
示例(故障監(jiān)控)
創(chuàng)建spring-cloud-consumer-user-hystrix-dashboard-9002子項目 來監(jiān)控spring-cloud-provider-user-hystrix-8004的運行情況。
1. 修改pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Spring Boot 測試依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--hystrix-dashboard 監(jiān)控的依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<!--添加 Spring Boot 的監(jiān)控模塊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2. 創(chuàng)建application.yml文件(類路徑resources目錄下)
server:
port: 9002 #端口號
#http://www.eureka7001.com:9002/hystrix 熔斷器監(jiān)控頁面
# localhost:8004//actuator/hystrix.stream 監(jiān)控地址
hystrix:
dashboard:
proxy-stream-allow-list:
- "localhost"
3. 在主啟動類上添加@EnableHystrixDashboard注解開啟Hystrix 監(jiān)控功能
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class ServiceCloudConsumerUserHystrixDashboard9002Application{
public static void main(String[] args) {
SpringApplication.run(ServiceCloudConsumerUserHystrixDashboard9002Application.class, args);
}
}
4. 創(chuàng)建HystrixDashboardConfig.java配置類
package com.sst.cx.config;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HystrixDashboardConfig {
// Hystrix dashboard 監(jiān)控界面必須配置
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");// 訪問路徑
registrationBean.setName("hystrix.stream");
return registrationBean;
}
}
5. 啟動spring-cloud-consumer-user-hystrix-dashboard-9002、spring-cloud-provider-user-hystrix-8004
在瀏覽器中訪問http://www.eureka7001.com:9002/hystrix,填入localhost:8004//actuator/hystrix.stream、2000、Hystrix Circuit,點擊下MonitorStream按鈕跳轉到監(jiān)控頁面,
在瀏覽器中多次訪問http://www.eureka7001.com:8004/user/hystrix/circuit/1、http://www.eureka7001.com:8004/user/hystrix/circuit/-1,查看Hystrix監(jiān)控頁面;

監(jiān)控頁面
2. SpringCloud Gateway(網(wǎng)關組件)
在微服務架構中,一個系統(tǒng)往往由多個微服務組成,而這些服務可能部署在不同機房、不同地區(qū)、不同域名下。這種情況下,客戶端(如:瀏覽器、手機、Postman軟件工具等)想要直接請求這些服務,就需要知道它們具體的地址信息,例如IP地址、端口號等。
這種客戶端直接請求服務的方式存在以下問題:
1. 當服務數(shù)量眾多時,客戶端需要維護大量的服務地址,這對于客戶端來說,是非常繁瑣復雜的。
2. 在某些場景下可能會存在跨域請求的問題。
3. 身份認證的難度大,每個微服務需要獨立認證。
可以通過API網(wǎng)關來解決這些問題。
API網(wǎng)關(一個搭建在客戶端和微服務之間的服務)
可以在API網(wǎng)關中處理一些非業(yè)務功能的邏輯(如:權限驗證、監(jiān)控、緩存、請求路由等)。
API網(wǎng)關就像整個微服務系統(tǒng)的門面一樣,是系統(tǒng)對外的唯一入口。有了它,客戶端會先將請求發(fā)送到 API 網(wǎng)關,然后由 API 網(wǎng)關根據(jù)請求的標識信息將請求轉發(fā)到微服務實例。
對于服務數(shù)量眾多、復雜度較高、規(guī)模比較大的系統(tǒng)來說,使用 API 網(wǎng)關具有以下好處:
1. 客戶端通過 API 網(wǎng)關與微服務交互時,客戶端只需要知道 API 網(wǎng)關地址即可,而不需要維護大量的服務地址,簡化了客戶端的開發(fā)。
2. 客戶端直接與 API 網(wǎng)關通信,能夠減少客戶端與各個服務的交互次數(shù)。
3. 客戶端與后端的服務耦合度降低。
4. 節(jié)省流量,提高性能,提升用戶體驗。
5. API 網(wǎng)關還提供了安全、流控、過濾、緩存、計費以及監(jiān)控等 API 管理功能。
常見的API網(wǎng)關實現(xiàn)方案:
1. Spring Cloud Gateway
2. Spring Cloud Netflix Zuul
3. Kong
4. Nginx+Lua
5. Traefik

兩種服務訪問方式對比
SpringCloud Gateway
基于Spring5.0、SpringBoot2.0、Project Reactor等技術開發(fā)的高性能API網(wǎng)關組件。Spring Cloud Gateway 是基于 WebFlux 框架實現(xiàn)的,而 WebFlux 框架底層則使用了高性能的 Reactor 模式通信框架 Netty。
旨在提供一種簡單而有效的途徑來發(fā)送API,并為它們提供橫切關注點(如:安全性、監(jiān)控/指標、彈性)。
核心概念(定義路由轉發(fā)規(guī)則)
1. Route(路由) Route和Predicate必須同時聲明
網(wǎng)關最基本的模塊。它由一個 ID、一個目標 URI、一組斷言(Predicate)和一組過濾器(Filter)組成。
2. Predicate(斷言,實現(xiàn)Route路由的匹配規(guī)則)
路由轉發(fā)的判斷條件(對HTTP請求進行匹配,如:請求方式、請求路徑、請求頭、參數(shù)等),如果請求與斷言匹配成功(滿足了Predicate的條件),則將請求轉發(fā)到指定的服務。
需要注意以下3點:
1. Route 路由與Predicate 斷言的對應關系為“一對多”,一個路由可以包含多個不同斷言。
2. 一個請求想要轉發(fā)到指定的路由上,就必須同時匹配路由上的所有斷言。
3. 當一個請求同時滿足多個路由的斷言條件時,請求只會被首個成功匹配的路由轉發(fā)。
3. Filter(過濾器)對請求進行攔截和修改、對上文的響應進行再處理
通常情況下,出于安全方面的考慮,服務端提供的服務往往都會有一定的校驗邏輯(如:用戶登陸狀態(tài)校驗、簽名校驗等)。在微服務架構中,系統(tǒng)由多個微服務組成,所有這些服務都需要這些校驗邏輯,可以將這些校驗邏輯寫到SpringCloudGateway的Filter過濾器中。
Filter的分類
1. Pre 類型
在請求被轉發(fā)到微服務之前可以對請求進行攔截和修改(如:參數(shù)校驗、權限校驗、流量監(jiān)控、日志輸出以及協(xié)議轉換等操作)。
2. Post 類型
在微服務對請求做出響應后可以對響應進行攔截和再處理(如:修改響應內(nèi)容或響應頭、日志輸出、流量監(jiān)控等)。
Filter的分類(按作用范圍)
1. GatewayFilter網(wǎng)關過濾器
作用于單個路由或者一組路由上的過濾器(對單個路由或者一組路由上傳入的請求和傳出響應進行攔截,并實現(xiàn)一些與業(yè)務無關的功能,比如登陸狀態(tài)校驗、簽名校驗、權限校驗、日志輸出、流量監(jiān)控等)。
GatewayFilter在配置文件中的寫法與Predicate類似,格式如下:
/*
spring:
cloud:
gateway:
routes:
- id: xxxx
uri: xxxx
predicates:
- Path=xxxx
filters:
- AddRequestParameter=X-Request-Id,1024 #過濾器工廠會在匹配的請求頭加上一對請求頭,名稱為 X-Request-Id 值為 1024
- PrefixPath=/user #在請求路徑前面加上 /user
……
*/
2. GlobalFilter
作用于所有的路由上的全局過濾器。
可以實現(xiàn)一些統(tǒng)一化的業(yè)務功能(如:權限認證、IP 訪問限制等)。當某個請求被路由匹配時,那么所有的 GlobalFilter 會和該路由自身配置的 GatewayFilter 組合成一個過濾器鏈。
提供了多種默認的GlobalFilter(如:與轉發(fā)、路由、負載均衡等相關的全局過濾器)。但在實際的項目開發(fā)中,通常都會自定義GlobalFilter全局過濾器以滿足自身業(yè)務需求,很少直接使用默認GlobalFilter。
動態(tài)路由
默認情況下,SpringCloudGateway會根據(jù)服務注冊中心(如:EurekaServer)中維護的服務列表,以服務名(spring.application.name)作為路徑創(chuàng)建動態(tài)路由進行轉發(fā),從而實現(xiàn)動態(tài)路由功能。
可以在配置文件中,將Route的uri地址修改為以下形式:
lb://service-name
說明:
1. lb:uri的協(xié)議(表示開啟SpringCloudGateway的負載均衡功能)。
2. service-name:服務名(SpringCloudGateway會根據(jù)它獲取到具體的微服務地址)。
特性:
1. 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 構建。
2. 能夠在任意請求屬性上匹配路由。
3. predicates(斷言) 和 filters(過濾器)是特定于路由的。
4. 集成了 Hystrix 熔斷器。
5. 集成了 Spring Cloud DiscoveryClient(服務發(fā)現(xiàn)客戶端)。
6. 易于編寫斷言和過濾器。
7. 能夠限制請求頻率。
8. 能夠重寫請求路徑。
工作流程:
1. 客戶端將請求發(fā)送到SpringCloudGateway上。
2. SpringCloudGateway通過 GatewayHandlerMapping 找到與請求相匹配的路由,將其發(fā)送給 GatewayWebHandler。
3. GatewayWebHandler通過指定的過濾器鏈(Filter Chain),將請求轉發(fā)到實際的服務節(jié)點中,執(zhí)行業(yè)務邏輯返回響應結果。
4. 過濾器之間用虛線分開是因為過濾器可能會在轉發(fā)請求之前(pre)或之后(post)執(zhí)行業(yè)務邏輯。
5. 過濾器(Filter)可以在請求被轉發(fā)到服務端前,對請求進行攔截和修改(如:參數(shù)校驗、權限校驗、流量監(jiān)控、日志輸出以及協(xié)議轉換等)。
6. 過濾器可以在響應返回客戶端之前,對響應進行攔截和再處理(如:修改響應內(nèi)容或響應頭、日志輸出、流量監(jiān)控等)。
7. 響應原路返回給客戶端。

SpringCloudGateway工作流程

Predicate斷言匹配
| 斷言 | 示例 | 說明 |
|---|---|---|
| Path | - Path=/user/list/** | 請求路徑必須與 /user/list/** 匹配。 |
| Before | - Before=2001-10-20T11:47:34.255+08:00[Asia/Shanghai] | 必須是 2001 年 10 月 20 日 11 時 47 分 34.255 秒之前的請求。 |
| After | - After=2001-10-20T11:47:34.255+08:00[Asia/Shanghai] | 必須是 2001 年 10 月 20 日 11 時 47 分 34.255 秒之后的請求。 |
| Between | - Between=2001-10-20T15:18:33.226+08:00[Asia/Shanghai],2001-10-20T15:23:33.226+08:00[Asia/Shanghai] | 必須是 2001 年 10 月 20 日 15 時 18 分 33.226 秒 到 2001 年 10 月 20 日 15 時 23 分 33.226 秒之間的請求。 |
| Cookie | - Cookie=name,com.sst.cx | 必須攜帶 Cookie 且 Cookie 的內(nèi)容為 name=com.sst.cx 的請求。 |
| Header | - Header=X-Request-Id,\d+ | 請求頭上必須攜帶屬性 X-Request-Id 且屬性值為整數(shù)的請求。 |
| Method | - Method=GET | 必須是GET請求。 |
| 路由過濾器 | 描述 | 參數(shù) | 使用示例 |
|---|---|---|---|
| AddRequestHeader | 攔截請求 并添加一個指定的請求頭參數(shù)。 | name:需要添加的請求頭參數(shù)的 key;value:需要添加的請求頭參數(shù)的value。 | - AddRequestHeader=my-request-header,1024 |
| AddRequestParameter | 攔截請求 并添加一個指定的請求參數(shù)。 | name:需要添加的請求參數(shù)的 key;value:需要添加的請求參數(shù)的 value。 | - AddRequestParameter=my-request-param,c.biancheng.net |
| AddResponseHeader | 攔截響應 并添加一個指定的響應頭參數(shù)。 | name:需要添加的響應頭的 key;value:需要添加的響應頭的 value。 | - AddResponseHeader=my-response-header,c.biancheng.net |
| PrefixPath | 攔截請求 并在請求路徑前增加一個指定的前綴。 | prefix:需要增加的路徑前綴。 | - PrefixPath=/consumer |
| PreserveHostHeader | 轉發(fā)請求時,保持客戶端的Host信息不變,然后將它傳遞到提供具體服務的微服務中。 | 無 | - PreserveHostHeader |
| RemoveRequestHeader | 移除請求頭中指定的參數(shù)。 | name:需要移除的請求頭的 key。 | - RemoveRequestHeader=my-request-header |
| RemoveResponseHeader | 移除響應頭中指定的參數(shù)。 | name:需要移除的響應頭。 | - RemoveResponseHeader=my-response-header |
| RemoveRequestParameter | 移除指定的請求參數(shù)。 | name:需要移除的請求參數(shù)。 | - RemoveRequestParameter=my-request-param |
| RequestSize | 配置請求體的大小,當請求體過大時,將會返回 413 Payload Too Large。 | maxSize:請求體的大小。 | - name: RequestSize args: maxSize: 5000000 |
示例(Predicate的使用)
創(chuàng)建spring-cloud-gateway-9527子項目
1. 修改pom.xml文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 注意:在gateway網(wǎng)關服務中不能引入spring-boot-starter-web依賴,否則會報錯 -->
<!-- SpringCloudGateway網(wǎng)關依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2. 創(chuàng)建application.yml(類路徑Resources目錄下)
server:
port: 9527 #端口號
spring:
application:
name: microServiceCloudGateway
cloud:
gateway: #網(wǎng)關路由配置
routes:
#將spring-cloud-provider-user-8001提供的服務隱藏起來,不暴露給客戶端,只給客戶端暴露API網(wǎng)關的地址9527
- id: provider_user_list_routh #路由id,沒有固定規(guī)則,但必須唯一,建議與服務名對應
uri: http://localhost:8001 #匹配后提供服務的路由地址
predicates:
#斷言條件(必選全部符合)
- Path=/user/list/** #路徑匹配(注意:Path 中 P 為大寫)
- Method=GET #僅GET請求
eureka:
instance:
instance-id: micro-service-cloud-gateway-9527
hostname: micro-service-cloud-gateway
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/
3. 依次啟動Eureka服務注冊中心集群、spring-cloud-provider-user-8001、spring-cloud-gateway-9527,
在瀏覽器訪問http://localhost:9527/user/list
示例(動態(tài)路由)
1. 修改pom.xml文件:
server:
port: 9527 #端口號
spring:
application:
name: microServiceCloudGateway #服務注冊中心注冊的服務名
cloud:
gateway: #網(wǎng)關路由配置
discovery:
locator:
enabled: true #默認值為 true,即默認開啟從注冊中心動態(tài)創(chuàng)建路由的功能,利用微服務名進行路由
routes:
#將spring-cloud-provider-user-8001提供的服務隱藏起來,不暴露給客戶端,只給客戶端暴露API網(wǎng)關的地址9527
- id: provider_user_list_routh #路由id,沒有固定規(guī)則,但唯一,建議與服務名對應
uri: lb://springCloudProviderUser #動態(tài)路由,使用服務名代替上面的具體帶端口 http://www.eureka7001.com:9527/user/list
predicates:
#斷言條件(必選全部符合)
- Path=/user/list/** #路徑匹配 注意:Path 中 P 為大寫
- Method=GET #只能時 GET 請求時,才能訪問
eureka:
instance:
instance-id: micro-service-cloud-gateway-9527
hostname: micro-service-cloud-gateway
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/
2. 重新啟動spring-cloud-gateway-9527,
在瀏覽器中訪問http://localhost:9527/user/list
示例(GatewayFilter網(wǎng)關過濾器)
1. 修改pom.xml文件
predicates:
- Path=/get/**
filters:
- PrefixPath=/user #在請求路徑上增加一個前綴 /user
2. 重新啟動spring-cloud-gateway-9527,
在瀏覽器中訪問http://localhost:9527/get/1
示例(GlobalFilter全局過濾器)
1. 創(chuàng)建MyGlobalFilter.java(自定義全局網(wǎng)關過濾器類)
package com.sst.cx.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
// 自定義全局網(wǎng)關過濾器(GlobalFilter)
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("進入自定義的全局過濾器 MyGlobalFilter" + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("參數(shù) uname 不能為 null!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 過濾器的順序,0 表示第一個
return 0;
}
}
2. 重新啟動spring-cloud-gateway-9527,
在瀏覽器訪問http://localhost:9527/user/list?uname=123456,不添加uname參數(shù)會被過濾掉。
3. SpringCloud Config(分布式配置組件)
在分布式微服務系統(tǒng)中,幾乎所有服務的運行都離不開配置文件的支持,這些配置文件通常由各個服務自行管理,以properties或yml 格式(如:application.properties、application.yml)保存在各個微服務的類路徑下。
這種將配置文件散落在各個服務中的管理方式,存在以下問題:
1. 管理難度大:配置文件散落在各個微服務中,難以管理。
2. 安全性低:配置跟隨源代碼保存在代碼庫中,容易造成配置泄漏。
3. 時效性差:微服務中的配置修改后,必須重啟服務,否則無法生效。
4. 局限性明顯:無法支持動態(tài)調整(如:日志開關、功能開關)。
使用配置中心(如:百度的Disconf、淘寶的diamond、360的QConf、攜程的Apollo、SpringCloud的Config)對配置進行統(tǒng)一管理可解決這些問題。
SpringCloudConfig
為微服務架構中各個微服務提供集中化的外部配置支持。即:將各個微服務的配置文件集中存儲在一個外部的存儲倉庫或系統(tǒng)(如:Git、SVN等)中,對配置統(tǒng)一管理。
分為2部分:
1. ConfigServer(分布式配置中心),它是一個獨立運行的微服務應用,用來連接配置倉庫并為客戶端提供獲取配置信息、加密信息和解密信息的訪問接口。
2. ConfigClient:微服務架構中的各個微服務,它們通過ConfigServer對配置進行管理,并從ConfigSever中獲取和加載配置信息。
Spring Cloud Config 默認使用 Git 存儲配置信息,因此使用 Spirng Cloud Config 構建的配置服務器天然就支持對微服務配置的版本管理。我們可以使用 Git 客戶端工具方便地對配置內(nèi)容進行管理和訪問。除了 Git 外,Spring Cloud Config 還提供了對其他存儲方式的支持,例如 SVN、本地化文件系統(tǒng)等。
工作流程:
1. 開發(fā)或運維人員提交配置文件到遠程的 Git 倉庫。
2. Config 服務端(分布式配置中心)負責連接配置倉庫 Git,并對 Config 客戶端暴露獲取配置的接口。
3. Config 客戶端通過 Config 服務端暴露出來的接口,拉取配置倉庫中的配置。
4. Config 客戶端獲取到配置信息,以支持服務的運行。
特點:
1. 與Spring的生態(tài)體系無縫集成。
2. 將所有微服務的配置文件集中存儲在一個外部的存儲倉庫或系統(tǒng)中統(tǒng)一管理。
3. Spring Cloud Config 配置中心將配置以 REST 接口的形式暴露給各個微服務,以方便各個微服務獲取。
4. 微服務可以通過 Spring Cloud Config 向配置中心統(tǒng)一拉取屬于它們自己的配置信息。
5. 當配置發(fā)生變化時,微服務不需要重啟即可感知到配置的變化,并自動獲取和應用最新配置。
6. 一個應用可能有多個環(huán)境,例如開發(fā)(dev)環(huán)境、測試(test)環(huán)境、生產(chǎn)(prod)環(huán)境等等,開發(fā)人員可以通過 Spring Cloud Config 對不同環(huán)境的各配置進行管理,且能夠確保應用在環(huán)境遷移后仍然有完整的配置支持其正常運行。
配置文件的訪問規(guī)則(在瀏覽器中可直接對配置文件進行訪問)
格式1. /{application}/{profile}[/{label}]
例:/config/dev/master
格式2. /{application}-{profile}.{suffix}
例:/config-dev.yml
格式3. /{label}/{application}-{profile}.{suffix}
例:/master/config-dev.yml
說明:
1. {application}:應用名(即:配置文件的名稱),如:config-dev。
2. {profile}:環(huán)境名,一個項目通常都有開發(fā)(dev)版本、測試(test)版本、生產(chǎn)(prod)版本,配置文件則以application-{profile}.yml 的形式進行區(qū)分,如:application-dev.yml、application-test.yml、application-prod.yml等。
3. {label}:Git分支名,默認:master分支,當訪問默認分支下的配置文件時,該參數(shù)可以省略,即第二種訪問方式。
4. {suffix}:配置文件的后綴,如:config-dev.yml的后綴為yml。

SpringCloudConfig 工作原理
示例(搭建Config服務端)
1. 在Github或Gitee上創(chuàng)建一個名為 springcloud-config 的倉庫(Repository),并獲取該倉庫的地址。
2. 創(chuàng)建spring-cloud-config-center-3344子項目,修改pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--配置中心服務器依賴-->
<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.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3. 創(chuàng)建application.yml(類路徑Resources目錄下)
server:
port: 3344 #端口號
spring:
application:
name: spring-cloud-config-center #服務名
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/springcloud-config.git
#倉庫名
search-paths:
- springcloud-config
force-pull: true
# 如果Git倉庫為公開倉庫,可以不填寫用戶名和密碼,如果是私有倉庫需要填寫
username: ********
password: ********
#分支名
label: master
eureka:
client: #將客戶端注冊到 eureka 服務列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務注冊到 Eureka 集群
4. 給主啟動類添加@EnableConfigServer注解開啟SpringCloudConfig配置中心功能。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class MicroServiceCloudConfigCenter3344Application {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConfigCenter3344Application.class, args);
}
}
5. 新建一個名為config-dev.yml文件,并將其上傳到 springcloud-config 倉庫 master 分支下。
config:
info: com.sst.cx
version: 1.0
6. 依次啟動服務注冊中心集群和 spring-cloud-config-center-3344,在瀏覽器中訪問http://localhost:3344/master/config-dev.yml 或 http://localhost:3344/config-dev.yml 或 http://localhost:3344/config/dev/master

瀏覽器中訪問配置文件
示例(搭建Config客戶端)
創(chuàng)建spring-cloud-config-client-3355子項目
1. 修改pom.xml文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Spring Cloud Config 客戶端依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud 2020.* 版本把bootstrap禁用,添加該依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2. 創(chuàng)建bootstrap.yml(類路徑Resources目錄下)
#bootstrap.yml 是系統(tǒng)級別的,加載優(yōu)先級高于 application.yml ,負責從外部加載配置并解析
server:
port: 3355 #端口號
spring:
application:
name: spring-cloud-config-client #服務名
cloud:
config:
label: master #分支名稱
name: config #配置文件名稱,config-dev.yml 中的 config
profile: dev #環(huán)境名 config-dev.yml 中的 dev
#這里不要忘記添加 http:// 否則無法讀取
uri: http://localhost:3344 #Spring Cloud Config 服務端(配置中心)地址
eureka:
client: #將客戶端注冊到 eureka 服務列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務注冊到 Eureka 集群
3. 創(chuàng)建ConfigClientController.java(通過該類獲取配置文件中的配置)
package com.sst.cx.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
// 讀取配置中心指定配置文件的內(nèi)容,并展示到頁面
@RestController
public class ConfigClientController {
@Value("${server.port}")
private String serverPort;
@Value("${config.info}")
private String configInfo;
@Value("${config.version}")
private String configVersion;
@GetMapping(value = "/getConfig")
public String getConfig() {
return "info:" + configInfo + "<br/>version:" + configVersion + "<br/>port:" + serverPort;
}
}
4. 給主啟動類添加@EnableEurekaClient注解開啟 Eureka客戶端功能。
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class MicroServiceCloudConfigClient3355Application {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudConfigClient3355Application.class, args);
}
}
5. 啟動服務注冊中心集群、spring-cloud-config-center-3344、spring-cloud-config-client-3355,在瀏覽器中訪問http://localhost:3355/getConfig
6. 將配置文件 config-dev.yml 中 config.version 的值修改為 2.0
7. 在瀏覽器中訪問http://localhost:3355/getConfig,可以看到version還是1.0;訪問http://localhost:3344/master/config-dev.yml,是2.0;
9. 重啟spring-cloud-config-client-3355,在瀏覽器中訪問http://localhost:3355/getConfig,可以看到是2.0了。
示例(不重啟Config客戶端的情況下,手動刷新配置 來獲取最新配置)
從上例中可知,配置文件更新后
1. SpringCloudConfig服務端可以直接從Git倉庫中獲取最新的配置。
2. SpringCloudConfig客戶端則需要重啟,否則無法通過SpringCloudConfig服務端獲取最新的配置。
通過在Config客戶端中引入SpringBootActuator監(jiān)控組件來監(jiān)控配置的變化,可以在不重啟Config客戶端的情況下獲取最新配置。
使用步驟:
1. 在spring-cloud-config-client-3355的pom.xml文件中添加
<!-- Spring Boot actuator 監(jiān)控模塊 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2. 在bootstrap.yml配置文件中添加(對外暴露SpringBootActuator的監(jiān)控節(jié)點):
# Spring Boot 2.50對 actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點,只暴露了 health 節(jié)點,本段配置(*)就是為了開啟所有的節(jié)點
management:
endpoints:
web:
exposure:
include: * # * 在yaml 文件屬于關鍵字,所以需要加引號
3. 在ConfigClientController類上添加@RefreshScope注解開啟配置刷新
4. 重啟spring-cloud-config-client-3355
修改config-dev.yml中的config.version為3.0
在瀏覽器中訪問http://localhost:3355/getConfig,還是2.0。
在終端執(zhí)行curl -X POST "http://localhost:3355/actuator/refresh"命令
在瀏覽器中訪問http://localhost:3355/getConfig,可以看到是3.0了。

SpringCloudCongfig手動刷新
這種方式帶來一個問題:只要配置倉庫中的配置發(fā)生改變,就需要挨個向Config客戶端手動發(fā)送POST請求,通知它們重新拉取配置。
在微服務架構中,一個系統(tǒng)往往包含十幾甚至幾十個服務,如果因為某一個配置文件的修改而向幾十個微服務發(fā)送POST請求,這顯然是不合理的。
SpringCloudBus(消息總線)
通過輕量級的消息代理(如:RabbitMQ、Kafka)(構建一個公共的消息主題Topic,默認為“springCloudBus”,這個Topic中的消息會被所有服務實例監(jiān)聽和消費。當其中的一個服務刷新數(shù)據(jù)時,SpringCloudBus會把信息保存到Topic中,這樣監(jiān)聽這個Topic的服務就收到消息并自動消費)將微服務架構中的各個服務連接起來,實現(xiàn)廣播狀態(tài)更改、事件推送等功能,還可以實現(xiàn)微服務之間的通信功能。
Config+Bus實現(xiàn)配置的動態(tài)刷新(一次通知,處處生效)
利用SpringCloudBus的特殊機制可以實現(xiàn)很多功能,如:配合SpringCloudConfig可以實現(xiàn)配置的動態(tài)刷新。當Git倉庫中的配置發(fā)生了改變,只需要向某一個服務(既可以是Config服務端,也可以是Config客戶端)發(fā)送一個 POST 請求,SpringCloudBus 就可以通過消息代理通知其他服務重新拉取最新配置,以實現(xiàn)配置的動態(tài)刷新。
工作流程:
1. 當 Git 倉庫中的配置發(fā)生改變后,運維人員向 Config 服務端發(fā)送一個 POST 請求,請求路徑為“/actuator/refresh”。
2. Config 服務端接收到請求后,會將該請求轉發(fā)給服務總線 Spring Cloud Bus。
3. Spring Cloud Bus 接到消息后,會通知給所有 Config 客戶端。
4. Config 客戶端接收到通知,請求 Config 服務端拉取最新配置。
5. 所有 Config 客戶端都獲取到最新的配置。

SpringCloudBus動態(tài)刷新配置的工作原理
示例(SpringCloudBus動態(tài)刷新配置)全局廣播
1. 在spring-cloud-config-center-3344的pom.xml文件中,添加
<!--添加消息總線(Bus)對 RabbitMQ 的支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--添加Spring Boot actuator 監(jiān)控模塊的依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2. 在spring-cloud-config-center-3344的application.xml文件中,spring下添加
##### RabbitMQ 相關配置,15672 是web 管理界面的端口,5672 是 MQ 的訪問端口###########
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
# Spring Boot 2.50對 actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點,只暴露了 heath 節(jié)點,本段配置(*)就是為了開啟所有的節(jié)點
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
3. 在spring-cloud-config-client-3355的pom.xml文件中,添加
<!--添加消息總線(Bus)對 RabbitMQ 的支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
4. 在spring-cloud-config-client-3355的bootstrap.xml文件中,spring下添加
##### RabbitMQ 相關配置,15672 是web 管理界面的端口,5672 是 MQ 的訪問端口###########
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
5. 參照spring-cloud-config-client-3355,創(chuàng)建spring-cloud-config-client-3366,修改bootstrap.yml(
bootstrap.yml是系統(tǒng)級別的,加載優(yōu)先級高于application.yml ,負責從外部加載配置并解析)
server:
port: 3366 #端口號為 3366
spring:
application:
name: spring-cloud-config-client-bus
cloud:
config:
label: master #分支名稱
name: config #配置文件名稱,config-dev.yml 中的 config
profile: dev #配置文件的后綴名 config-dev.yml 中的 dev
#這里不要忘記添加 http:// 否則無法讀取
uri: http://localhost:3344 #spring cloud 配置中心地址
##### RabbitMQ 相關配置,15672 是web 管理界面的端口,5672 是 MQ 的訪問端口###########
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
###################### eureka 配置 ####################
eureka:
client: #將客戶端注冊到 eureka 服務列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務注冊到 Eureka 集群
# Spring Boot 2.50對 actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點,只暴露了 heath 節(jié)點,本段配置(*)就是為了開啟所有的節(jié)點
management:
endpoints:
web:
exposure:
include: "*" # * 在yaml 文件屬于關鍵字,所以需要加引號
6. 啟動spring-cloud-config-center-3344、spring-cloud-config-client-3355、spring-cloud-config-client-3366,
在瀏覽器中訪問http://localhost:3355/getConfig、http://localhost:3366/getConfig
7. 將配置文件 config-dev.yml 中 config.version 的值修改為 4.0
8. 在終端執(zhí)行curl -X POST "http://localhost:3344/actuator/bus-refresh"
在瀏覽器中訪問http://localhost:3355/getConfig、http://localhost:3366/getConfig
可能遇到的錯誤:
1. 執(zhí)行curl命令405(Method Not Allowed)
原因:springcloud版本過高,降低版本,或者bus-refresh改為busrefresh
2. 執(zhí)行curl命令500(Internal Server Error)
【存疑:改為busrefresh后報500】
示例(SpringCloudBus動態(tài)刷新配置)定點通知
定點通知:不再通知所有的Config客戶端,而是根據(jù)需求只通知指定Config客戶端。
只要我們在發(fā)送POST請求時使用以下格式即可:
http://{hostname}:{port}/actuator/bus-refresh/{destination}
說明:
1. {hostname}:表示Config服務端的主機地址(域名 或 IP地址)。
2. {port}:表示Config服務端的端口號。
3. {destination}:表示需要定點通知的Config客戶端(微服務)。由Config客戶端的服務名(spring.application.name)+冒號+端口號(server.port)組成。
例:curl -X POST "http://localhost:3344/actuator/bus-refresh/spring-cloud-config-client:3355"