目錄
1. SpringCloud Hystrix(服務(wù)熔斷與降級(jí)組件 / 服務(wù)容錯(cuò)與保護(hù)組件)
2. SpringCloud Gateway(網(wǎng)關(guān)組件)
3. SpringCloud Config(分布式配置組件)
1. SpringCloud Hystrix(服務(wù)熔斷與降級(jí)組件 / 服務(wù)容錯(cuò)與保護(hù)組件)
在微服務(wù)架構(gòu)中您旁,一個(gè)應(yīng)用往往由多個(gè)服務(wù)組成烙常,這些服務(wù)之間相互依賴,依賴關(guān)系錯(cuò)綜復(fù)雜鹤盒。
通常情況下蚕脏,一個(gè)用戶請(qǐng)求往往需要多個(gè)服務(wù)配合才能完成。例如上圖所示昨悼,在所有服務(wù)都處于可用狀態(tài)時(shí)蝗锥,請(qǐng)求 1 需要調(diào)用 A、D率触、E终议、F 四個(gè)服務(wù)才能完成,請(qǐng)求 2 需要調(diào)用 B、E穴张、D 三個(gè)服務(wù)才能完成细燎,請(qǐng)求 3 需要調(diào)用服務(wù) C、F皂甘、E玻驻、D 四個(gè)服務(wù)才能完成。
當(dāng)服務(wù) E 發(fā)生故障或網(wǎng)絡(luò)延遲時(shí)偿枕,會(huì)出現(xiàn)以下情況:
1. 即使其他所有服務(wù)都可用璧瞬,由于服務(wù) E 的不可用,那么用戶請(qǐng)求 1渐夸、2嗤锉、3 都會(huì)處于阻塞狀態(tài),等待服務(wù) E 的響應(yīng)墓塌。在高并發(fā)的場(chǎng)景下瘟忱,會(huì)導(dǎo)致整個(gè)服務(wù)器的線程資源在短時(shí)間內(nèi)迅速消耗殆盡。
2. 所有依賴于服務(wù) E 的其他服務(wù)苫幢,例如服務(wù) B访诱、D 以及 F 也都會(huì)處于線程阻塞狀態(tài),等待服務(wù) E 的響應(yīng)韩肝,導(dǎo)致這些服務(wù)的不可用触菜。
3. 所有依賴服務(wù)B、D 和 F 的服務(wù)哀峻,例如服務(wù) A 和服務(wù) C 也會(huì)處于線程阻塞狀態(tài)玫氢,以等待服務(wù) D 和服務(wù) F 的響應(yīng),導(dǎo)致服務(wù) A 和服務(wù) C 也不可用谜诫。
當(dāng)微服務(wù)系統(tǒng)的一個(gè)服務(wù)出現(xiàn)故障時(shí),故障會(huì)沿著服務(wù)的調(diào)用鏈路在系統(tǒng)中瘋狂蔓延攻旦,最終導(dǎo)致整個(gè)微服務(wù)系統(tǒng)的癱瘓喻旷,這就是【雪崩效應(yīng)】。為了防止此類事件的發(fā)生牢屋,微服務(wù)架構(gòu)引入了“熔斷器”的一系列服務(wù)容錯(cuò)和保護(hù)機(jī)制且预。
熔斷器(Circuit Breaker)
與物理學(xué)中的熔斷器作用(當(dāng)線路出現(xiàn)故障時(shí)送丰,迅速切斷電源以保護(hù)電路的安全)相似瘪菌,微服務(wù)架構(gòu)中的熔斷器能夠在某個(gè)服務(wù)發(fā)生故障后,向服務(wù)調(diào)用方返回一個(gè)符合預(yù)期的帝际、可處理的降級(jí)響應(yīng)(FallBack)截酷,而不是長(zhǎng)時(shí)間的等待或者拋出調(diào)用方無法處理的異常涮拗。這樣就保證了服務(wù)調(diào)用方的線程不會(huì)被長(zhǎng)時(shí)間、不必要地占用,避免故障在微服務(wù)系統(tǒng)中的蔓延三热,防止系統(tǒng)雪崩效應(yīng)的發(fā)生鼓择。
Hystrix(服務(wù)容錯(cuò)與保護(hù)組件)讓服務(wù)擁有自我保護(hù)的能力
1. 保護(hù)線程資源
防止單個(gè)服務(wù)的故障耗盡系統(tǒng)中的所有線程資源。
2. 快速失敗機(jī)制
當(dāng)某個(gè)服務(wù)發(fā)生了故障就漾,不讓服務(wù)調(diào)用方一直等待呐能,而是直接返回請(qǐng)求失敗。
3. 監(jiān)控功能
提供熔斷器故障監(jiān)控組件HystrixDashboard(隨時(shí)監(jiān)控熔斷器的狀態(tài))抑堡。Hystrix 會(huì)持續(xù)地記錄所有通過 Hystrix 發(fā)起的請(qǐng)求的執(zhí)行信息摆出,并以統(tǒng)計(jì)報(bào)表的形式展示給用戶,包括每秒執(zhí)行請(qǐng)求的數(shù)量首妖、成功請(qǐng)求的數(shù)量和失敗請(qǐng)求的數(shù)量等偎漫。
4. 服務(wù)降級(jí) FallBack(保證當(dāng)前服務(wù)不受其他服務(wù)故障的影響,提高服務(wù)的健壯性)
(既可以在服務(wù)端悯搔,也可以在客戶端)提供一個(gè)請(qǐng)求失敗后的降級(jí)方案(通常是一個(gè)兜底方法骑丸,當(dāng)請(qǐng)求失敗后即調(diào)用該方法)。
使用場(chǎng)景:
1. 在服務(wù)器壓力劇增時(shí)妒貌,根據(jù)實(shí)際業(yè)務(wù)情況及流量通危,對(duì)一些不重要、不緊急的服務(wù)進(jìn)行有策略地不處理或簡(jiǎn)單處理灌曙,從而釋放服務(wù)器資源以保證核心服務(wù)正常運(yùn)作菊碟。
2. 當(dāng)某些服務(wù)不可用時(shí),為了避免長(zhǎng)時(shí)間等待造成服務(wù)卡頓或雪崩效應(yīng)在刺,而主動(dòng)執(zhí)行備用的降級(jí)邏輯立刻返回一個(gè)友好的提示逆害,以保障主體業(yè)務(wù)不受影響。
3. 程序運(yùn)行異常蚣驼、服務(wù)超時(shí)魄幕、熔斷器處于打開狀態(tài)、線程池資源耗盡颖杏。
使用步驟:
重寫HystrixCommand的getFallBack()方法 或 HystrixObservableCommand的resumeWithFallback()方法來使服務(wù)支持降級(jí)纯陨。
5. 服務(wù)熔斷(防止故障擴(kuò)散到其他服務(wù))
為了應(yīng)對(duì)雪崩效應(yīng)而出現(xiàn)的一種微服務(wù)鏈路保護(hù)機(jī)制。
Hystrix會(huì)監(jiān)控微服務(wù)間調(diào)用的狀況留储,當(dāng)某個(gè)微服務(wù)不可用 或 響應(yīng)時(shí)間太長(zhǎng) 或 失敗調(diào)用到一定比例時(shí),為了保護(hù)系統(tǒng)的整體可用性获讳,就會(huì)啟動(dòng)熔斷機(jī)制(暫時(shí)切斷請(qǐng)求對(duì)該服務(wù)的調(diào)用阴颖,并快速返回一個(gè)友好的錯(cuò)誤響應(yīng))。這種熔斷狀態(tài)不是永久的丐膝,在經(jīng)歷了一定的時(shí)間后量愧,熔斷器會(huì)再次檢測(cè)該微服務(wù)是否恢復(fù)正常钾菊,若服務(wù)恢復(fù)正常則恢復(fù)其調(diào)用鏈路。
熔斷狀態(tài)(3種):
1. 熔斷關(guān)閉狀態(tài)(Closed)
當(dāng)服務(wù)訪問正常時(shí)侠畔,熔斷器處于關(guān)閉狀態(tài)结缚,服務(wù)調(diào)用方可以正常地對(duì)服務(wù)進(jìn)行調(diào)用。
2. 熔斷開啟狀態(tài)(Open)
默認(rèn)情況下软棺,在固定時(shí)間內(nèi)接口調(diào)用出錯(cuò)比率達(dá)到一個(gè)閾值(例如 50%)红竭,熔斷器會(huì)進(jìn)入熔斷開啟狀態(tài)。進(jìn)入熔斷狀態(tài)后喘落,后續(xù)對(duì)該服務(wù)的調(diào)用都會(huì)被切斷茵宪,熔斷器會(huì)執(zhí)行本地的降級(jí)(FallBack)方法。
3. 半熔斷狀態(tài)(Half-Open)
在熔斷開啟一段時(shí)間之后瘦棋,熔斷器會(huì)進(jìn)入半熔斷狀態(tài)稀火。在半熔斷狀態(tài)下,熔斷器會(huì)嘗試恢復(fù)服務(wù)調(diào)用方對(duì)服務(wù)的調(diào)用赌朋,允許部分請(qǐng)求調(diào)用該服務(wù)凰狞,并監(jiān)控其調(diào)用成功率。如果成功率達(dá)到預(yù)期沛慢,則說明服務(wù)已恢復(fù)正常赡若,熔斷器進(jìn)入關(guān)閉狀態(tài);如果成功率仍舊很低团甲,則重新進(jìn)入熔斷開啟狀態(tài)逾冬。
熔斷流程:
1. 當(dāng)服務(wù)的調(diào)用出錯(cuò)率達(dá)到或超過Hystix規(guī)定的比率(默認(rèn)為50%)后,熔斷器進(jìn)入熔斷開啟狀態(tài)躺苦。
2. 熔斷器進(jìn)入熔斷開啟狀態(tài)后身腻,Hystrix會(huì)啟動(dòng)一個(gè)休眠時(shí)間窗,在這個(gè)時(shí)間窗內(nèi)匹厘,該服務(wù)的降級(jí)邏輯會(huì)臨時(shí)充當(dāng)業(yè)務(wù)主邏輯嘀趟,而原來的業(yè)務(wù)主邏輯不可用。
3. 當(dāng)有請(qǐng)求再次調(diào)用該服務(wù)時(shí)愈诚,會(huì)直接調(diào)用降級(jí)邏輯快速地返回失敗響應(yīng)去件,以避免系統(tǒng)雪崩。
4. 當(dāng)休眠時(shí)間窗到期后扰路,Hystrix 會(huì)進(jìn)入半熔斷轉(zhuǎn)態(tài),允許部分請(qǐng)求對(duì)服務(wù)原來的主業(yè)務(wù)邏輯進(jìn)行調(diào)用倔叼,并監(jiān)控其調(diào)用成功率汗唱。
5. 如果調(diào)用成功率達(dá)到預(yù)期,則說明服務(wù)已恢復(fù)正常丈攒,Hystrix 進(jìn)入熔斷關(guān)閉狀態(tài)哩罪,服務(wù)原來的主業(yè)務(wù)邏輯恢復(fù)授霸;否則 Hystrix 重新進(jìn)入熔斷開啟狀態(tài),休眠時(shí)間窗口重新計(jì)時(shí)际插,繼續(xù)重復(fù)第 2 到第 5 步碘耳。
SpringCloudHystrix
對(duì)Netflix公司的Hystrix開源組件進(jìn)行二次封裝,提供了熔斷器功能框弛,能夠有效地阻止分布式微服務(wù)系統(tǒng)中出現(xiàn)聯(lián)動(dòng)故障辛辨,以提高微服務(wù)系統(tǒng)的彈性。
具有服務(wù)降級(jí)瑟枫、服務(wù)熔斷斗搞、線程隔離、請(qǐng)求緩存慷妙、請(qǐng)求合并以及實(shí)時(shí)故障監(jiān)控等強(qiáng)大功能僻焚。
@HystrixCommand注解的commandProperties屬性的參數(shù)
1. metrics.rollingStats.timeInMilliseconds(統(tǒng)計(jì)時(shí)間窗)
監(jiān)控統(tǒng)計(jì)時(shí)間窗內(nèi)的服務(wù)調(diào)用出錯(cuò)率。
2. circuitBreaker.sleepWindowInMilliseconds(休眠時(shí)間窗)
熔斷開啟狀態(tài)持續(xù)一段時(shí)間后膝擂,熔斷器會(huì)自動(dòng)進(jìn)入半熔斷狀態(tài)虑啤,這段時(shí)間就被稱為休眠窗口期。
3. circuitBreaker.requestVolumeThreshold(請(qǐng)求總數(shù)閥值)
在統(tǒng)計(jì)時(shí)間窗內(nèi)架馋,請(qǐng)求總數(shù)必須到達(dá)一定的數(shù)量級(jí)狞山,Hystrix 才可能會(huì)將熔斷器打開進(jìn)入熔斷開啟轉(zhuǎn)態(tài),而這個(gè)請(qǐng)求數(shù)量級(jí)就是 請(qǐng)求總數(shù)閥值绩蜻。Hystrix 請(qǐng)求總數(shù)閾值默認(rèn)為 20铣墨,這就意味著在統(tǒng)計(jì)時(shí)間窗內(nèi),如果服務(wù)調(diào)用次數(shù)不足 20 次办绝,即使所有的請(qǐng)求都調(diào)用出錯(cuò)伊约,熔斷器也不會(huì)打開。
4. circuitBreaker.errorThresholdPercentage(錯(cuò)誤百分比閾值)
當(dāng)請(qǐng)求總數(shù)在統(tǒng)計(jì)時(shí)間窗內(nèi)超過了請(qǐng)求總數(shù)閥值孕蝉,且請(qǐng)求調(diào)用出錯(cuò)率超過一定的比例屡律,熔斷器才會(huì)打開進(jìn)入熔斷開啟轉(zhuǎn)態(tài),而這個(gè)比例就是錯(cuò)誤百分比閾值降淮。錯(cuò)誤百分比閾值設(shè)置為 50超埋,就表示錯(cuò)誤百分比為 50%,如果服務(wù)發(fā)生了 30 次調(diào)用佳鳖,其中有 15 次發(fā)生了錯(cuò)誤霍殴,即超過了 50% 的錯(cuò)誤百分比,這時(shí)候?qū)⑷蹟嗥骶蜁?huì)打開系吩。
示例(服務(wù)端降級(jí))
以之前的例子為基礎(chǔ)
1. 創(chuàng)建spring-cloud-provider-user-hystrix-8004服務(wù)提供者来庭,修改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 #服務(wù)端口號(hào)
spring:
application:
name: springCloudProviderUserHystrix #對(duì)外暴露的微服務(wù)名稱
jackson:
serialization:
FAIL_ON_EMPTY_BEANS: false
######### SpringCloud自定義服務(wù)名稱和ip地址 #############
eureka:
client: #將客戶端注冊(cè)到eureka服務(wù)列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #該地址為 7001注冊(cè)中心在application.yml中暴露出來的注冊(cè)地址 (單機(jī)版)
instance:
instance-id: spring-cloud-provider-8004 #自定義服務(wù)名稱信息
prefer-ip-address: true #顯示訪問路徑的ip地址
########### SpringCloud使用 SpringBootActuator 監(jiān)控完善信息 ########
# SpringBoot2.50對(duì) actuator監(jiān)控屏蔽了大多數(shù)的節(jié)點(diǎn),只暴露了heath節(jié)點(diǎn)穿挨,本段配置(*)就是為了開啟所有的節(jié)點(diǎn)
management:
endpoints:
web:
exposure:
include: "*" # * 在yaml 文件屬于關(guān)鍵字月弛,所以需要加引號(hào)
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 熔斷器超時(shí)
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;
}
// 一旦該方法失敗并拋出了異常信息后肴盏,會(huì)自動(dòng)調(diào)用@HystrixCommand注解標(biāo)注的fallbackMethod指定的方法(即指定降級(jí)方法)
@HystrixCommand(fallbackMethod = "user_TimeoutHandler",
commandProperties =
// 規(guī)定5秒鐘以內(nèi)就不報(bào)錯(cuò),正常運(yùn)行帽衙,超過5秒就報(bào)錯(cuò)菜皂,調(diào)用指定的方法
{@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 + " 耗時(shí): " + outTime;
}
// 當(dāng)服務(wù)出現(xiàn)故障后,調(diào)用該方法給出友好提示
public String user_TimeoutHandler(Integer id) {
return "系統(tǒng)繁忙請(qǐ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("端口號(hào):" + serverPort + " result:" + result);
return result + "恍飘, 端口號(hào):" + serverPort;
}
// Hystrix服務(wù)超時(shí)降級(jí)
@RequestMapping(value = "/user/hystrix/timeout/{id}")
public String userInfo_Timeout(@PathVariable("id") Integer id) {
String result = userService.userInfo_Timeout(id);
log.info("端口號(hào):" + serverPort + " result:" + result);
return result + ", 端口號(hào):" + serverPort;
}
}
6. 創(chuàng)建主啟動(dò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. 依次啟動(dòng)服務(wù)注冊(cè)中心集群冀泻、spring-cloud-provider-user-hystrix-8004常侣,
在瀏覽器中訪問http://www.eureka7001.com:8004/user/hystrix/ok/1(正常訪問)、http://www.eureka7001.com:8004/user/hystrix/timeout/1(服務(wù)降級(jí))
示例(客戶端降級(jí))
對(duì)spring-cloud-feign-user-80項(xiàng)目添加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文件中添加
####### 配置所有請(qǐng)求超時(shí)時(shí)間(單位為毫秒) ##########
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 7000
####### 配置具體請(qǐng)求超時(shí)時(shí)間 ########
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);
}
// 在客戶端進(jìn)行降級(jí)
@RequestMapping(value = "/consumer/user/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "user_TimeoutHandler") // 為該請(qǐng)求指定專屬的回退方法
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 出錯(cuò)弹渔,服務(wù)已被降級(jí)胳施!");
return "服務(wù)端系統(tǒng)繁忙,請(qǐng)稍后再試Vā(客戶端 userInfo_Timeout 專屬的回退方法觸發(fā))";
}
}
5. 在主啟動(dòng)類上添加@EnableHystrix注解啟用Hystrix舞肆。
6. 修改spring-cloud-provider-user-hystrix-8004的UserServiceImpl.java的userInfo_Timeout()方法的outTime改為4(讓客戶端來處理降級(jí))。
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:為包含該服務(wù)方法的類的名稱(通常為服務(wù)綁定接口的名稱)椿胯,例如 UserHystrixService 接口。
yyy:服務(wù)方法名剃根,例如 userInfo_Timeout() 方法哩盲。
zzz:方法內(nèi)的參數(shù)類型,例如 Integer狈醉、String 等等
mmm:要設(shè)置的超時(shí)時(shí)間廉油,單位為毫秒(1 秒 =1000 毫秒)
*/
【存疑:配置具體請(qǐng)求超時(shí)時(shí)間無效,正常請(qǐng)求未降級(jí)】
示例(全局降級(jí)方法)
通過上面的方式實(shí)現(xiàn)服務(wù)降級(jí)時(shí)苗傅,需要針對(duì)所有業(yè)務(wù)方法都配置降級(jí)方法抒线,這極有可能會(huì)造成代碼的急劇膨脹。為了解決該問題渣慕,可以為所有業(yè)務(wù)方法指定一個(gè)全局的回退方法嘶炭。
全局降級(jí)方法的優(yōu)先級(jí)較低,只有業(yè)務(wù)方法沒有指定其降級(jí)方法時(shí)逊桦,服務(wù)降級(jí)時(shí)才會(huì)觸發(fā)全局回退方法眨猎。若業(yè)務(wù)方法指定它自己的回退方法,那么在服務(wù)降級(jí)時(shí)强经,就只會(huì)直接觸發(fā)它自己的回退方法宵呛,而非全局回退方法。
HystrixController_Consumer.java中
1. 給該類添加@DefaultProperties注解夕凝,通過其defaultFallback屬性指定一個(gè)全局的降級(jí)方法宝穗。
@DefaultProperties(defaultFallback = "user_Global_FallbackMethod") // 全局的服務(wù)降級(jí)方法
2. 創(chuàng)建該方法。
public String user_Global_FallbackMethod() {
return "運(yùn)行出錯(cuò)或服務(wù)端系統(tǒng)繁忙码秉,請(qǐng)稍后再試4(客戶端全局回退方法觸發(fā),)";
}
3. 在所有的業(yè)務(wù)方法上都標(biāo)注@HystrixCommand注解。
重啟spring-cloud-feign-user-80,
在瀏覽器中訪問http://www.eureka7001.com/consumer/user/hystrix/timeout/1
【存疑:配置具體請(qǐng)求超時(shí)時(shí)間無效转砖,正常請(qǐng)求未降級(jí)】
示例(解耦降級(jí)邏輯)
不管是業(yè)務(wù)方法指定的降級(jí)方法還是全局降級(jí)方法须鼎,它們都必須和業(yè)務(wù)方法在同一個(gè)類中才能生效,業(yè)務(wù)邏輯與降級(jí)邏輯耦合度極高府蔗。
1. 創(chuàng)建UserHystrixFallBackService.java,實(shí)現(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)繁忙晋控,請(qǐng)稍后重試!(解耦回退方法觸發(fā))-----------------------";
}
@Override
public String userInfo_Timeout(Integer id) {
return "--------------------系統(tǒng)繁忙姓赤,請(qǐng)稍后重試I囊搿(解耦回退方法觸發(fā))-----------------------";
}
}
2. 修改UserHystrixService.java的@FeignClient注解,添加fallback屬性為UserHystrixFallBackService.class
@FeignClient(value = "springCloudProviderUserHystrix",fallback = UserHystrixFallBackService.class)
3. 去除HystrixController_Consumer.java的@DefaultProperties注解不铆、降級(jí)方法蝌焚。
4. 重啟spring-cloud-feign-user-80,
在瀏覽器中訪問http://www.eureka7001.com/consumer/user/hystrix/timeout/1
【存疑:配置具體請(qǐng)求超時(shí)時(shí)間無效,正常請(qǐng)求未降級(jí)】
示例(驗(yàn)證熔斷機(jī)制)
1. 在spring-cloud-provider-user-hystrix-8004的UserService接口中誓斥,添加一個(gè)userCircuitBreaker()方法:
// hystrix 熔斷機(jī)制
public String userCircuitBreaker(Integer id);
2. 在UserServiceImpl.java中只洒,添加userCircuitBreaker()方法實(shí)現(xiàn):
//
@Override
@HystrixCommand(fallbackMethod = "userCircuitBreaker_fallback", commandProperties = {
// 以下參數(shù)在 HystrixCommandProperties 類中有默認(rèn)配置
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否開啟熔斷器
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1000"), // 統(tǒng)計(jì)時(shí)間窗
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 統(tǒng)計(jì)時(shí)間窗內(nèi)請(qǐng)求次數(shù)
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 休眠時(shí)間窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), // 在統(tǒng)計(jì)時(shí)間窗口期以內(nèi),請(qǐng)求失敗率達(dá)到 60%時(shí)進(jìn)入熔斷狀態(tài)
})
public String userCircuitBreaker(Integer id) {
if (id < 0) {
// 當(dāng)傳入的 id 為負(fù)數(shù)時(shí)劳坑,拋出異常毕谴,調(diào)用降級(jí)方法
throw new RuntimeException("id 不能是負(fù)數(shù)!");
}
return Thread.currentThread().getName() + "\t" + "調(diào)用成功";
}
public String userCircuitBreaker_fallback(Integer id) {
return "id 不能是負(fù)數(shù),請(qǐng)稍后重試!\t id:" + id;
}
3. 在UserController.java中距芬,添加userCircuitBreaker()方法(對(duì)外提供服務(wù)):
// Hystrix服務(wù)熔斷
@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涝开;
多次請(qǐng)求http://www.eureka7001.com:8004/user/hystrix/circuit/-1,使調(diào)用出錯(cuò)率大于錯(cuò)誤百分比閥值蔑穴;
多次請(qǐng)求http://www.eureka7001.com:8004/user/hystrix/circuit/1忠寻,當(dāng)服務(wù)調(diào)用正確率上升到一定的利率后,Hystrix 進(jìn)入熔斷關(guān)閉狀態(tài)存和。
【存疑:一直沒進(jìn)入熔斷狀態(tài)】
示例(故障監(jiān)控)
創(chuàng)建spring-cloud-consumer-user-hystrix-dashboard-9002子項(xiàng)目 來監(jiān)控spring-cloud-provider-user-hystrix-8004的運(yùn)行情況奕剃。
1. 修改pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Spring Boot 測(cè)試依賴-->
<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 #端口號(hào)
#http://www.eureka7001.com:9002/hystrix 熔斷器監(jiān)控頁(yè)面
# localhost:8004//actuator/hystrix.stream 監(jiān)控地址
hystrix:
dashboard:
proxy-stream-allow-list:
- "localhost"
3. 在主啟動(dòng)類上添加@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. 啟動(dòng)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茄袖,點(diǎn)擊下MonitorStream按鈕跳轉(zhuǎn)到監(jiān)控頁(yè)面操软,
在瀏覽器中多次訪問http://www.eureka7001.com:8004/user/hystrix/circuit/1、http://www.eureka7001.com:8004/user/hystrix/circuit/-1宪祥,查看Hystrix監(jiān)控頁(yè)面聂薪;
2. SpringCloud Gateway(網(wǎng)關(guān)組件)
在微服務(wù)架構(gòu)中家乘,一個(gè)系統(tǒng)往往由多個(gè)微服務(wù)組成,而這些服務(wù)可能部署在不同機(jī)房藏澳、不同地區(qū)仁锯、不同域名下。這種情況下翔悠,客戶端(如:瀏覽器业崖、手機(jī)、Postman軟件工具等)想要直接請(qǐng)求這些服務(wù)蓄愁,就需要知道它們具體的地址信息双炕,例如IP地址、端口號(hào)等撮抓。
這種客戶端直接請(qǐng)求服務(wù)的方式存在以下問題:
1. 當(dāng)服務(wù)數(shù)量眾多時(shí)妇斤,客戶端需要維護(hù)大量的服務(wù)地址,這對(duì)于客戶端來說胀滚,是非常繁瑣復(fù)雜的趟济。
2. 在某些場(chǎng)景下可能會(huì)存在跨域請(qǐng)求的問題。
3. 身份認(rèn)證的難度大咽笼,每個(gè)微服務(wù)需要獨(dú)立認(rèn)證顷编。
可以通過API網(wǎng)關(guān)來解決這些問題。
API網(wǎng)關(guān)(一個(gè)搭建在客戶端和微服務(wù)之間的服務(wù))
可以在API網(wǎng)關(guān)中處理一些非業(yè)務(wù)功能的邏輯(如:權(quán)限驗(yàn)證剑刑、監(jiān)控媳纬、緩存、請(qǐng)求路由等)施掏。
API網(wǎng)關(guān)就像整個(gè)微服務(wù)系統(tǒng)的門面一樣钮惠,是系統(tǒng)對(duì)外的唯一入口。有了它七芭,客戶端會(huì)先將請(qǐng)求發(fā)送到 API 網(wǎng)關(guān)素挽,然后由 API 網(wǎng)關(guān)根據(jù)請(qǐng)求的標(biāo)識(shí)信息將請(qǐng)求轉(zhuǎn)發(fā)到微服務(wù)實(shí)例。
對(duì)于服務(wù)數(shù)量眾多狸驳、復(fù)雜度較高预明、規(guī)模比較大的系統(tǒng)來說,使用 API 網(wǎng)關(guān)具有以下好處:
1. 客戶端通過 API 網(wǎng)關(guān)與微服務(wù)交互時(shí)耙箍,客戶端只需要知道 API 網(wǎng)關(guān)地址即可撰糠,而不需要維護(hù)大量的服務(wù)地址,簡(jiǎn)化了客戶端的開發(fā)辩昆。
2. 客戶端直接與 API 網(wǎng)關(guān)通信阅酪,能夠減少客戶端與各個(gè)服務(wù)的交互次數(shù)。
3. 客戶端與后端的服務(wù)耦合度降低。
4. 節(jié)省流量术辐,提高性能砚尽,提升用戶體驗(yàn)。
5. API 網(wǎng)關(guān)還提供了安全辉词、流控尉辑、過濾、緩存较屿、計(jì)費(fèi)以及監(jiān)控等 API 管理功能。
常見的API網(wǎng)關(guān)實(shí)現(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等技術(shù)開發(fā)的高性能API網(wǎng)關(guān)組件。Spring Cloud Gateway 是基于 WebFlux 框架實(shí)現(xiàn)的襟企,而 WebFlux 框架底層則使用了高性能的 Reactor 模式通信框架 Netty嘱么。
旨在提供一種簡(jiǎn)單而有效的途徑來發(fā)送API,并為它們提供橫切關(guān)注點(diǎn)(如:安全性顽悼、監(jiān)控/指標(biāo)曼振、彈性)。
核心概念(定義路由轉(zhuǎn)發(fā)規(guī)則)
1. Route(路由) Route和Predicate必須同時(shí)聲明
網(wǎng)關(guān)最基本的模塊蔚龙。它由一個(gè) ID冰评、一個(gè)目標(biāo) URI、一組斷言(Predicate)和一組過濾器(Filter)組成木羹。
2. Predicate(斷言甲雅,實(shí)現(xiàn)Route路由的匹配規(guī)則)
路由轉(zhuǎn)發(fā)的判斷條件(對(duì)HTTP請(qǐng)求進(jìn)行匹配,如:請(qǐng)求方式坑填、請(qǐng)求路徑抛人、請(qǐng)求頭、參數(shù)等)脐瑰,如果請(qǐng)求與斷言匹配成功(滿足了Predicate的條件)妖枚,則將請(qǐng)求轉(zhuǎn)發(fā)到指定的服務(wù)。
需要注意以下3點(diǎn):
1. Route 路由與Predicate 斷言的對(duì)應(yīng)關(guān)系為“一對(duì)多”苍在,一個(gè)路由可以包含多個(gè)不同斷言绝页。
2. 一個(gè)請(qǐng)求想要轉(zhuǎn)發(fā)到指定的路由上,就必須同時(shí)匹配路由上的所有斷言忌穿。
3. 當(dāng)一個(gè)請(qǐng)求同時(shí)滿足多個(gè)路由的斷言條件時(shí)抒寂,請(qǐng)求只會(huì)被首個(gè)成功匹配的路由轉(zhuǎn)發(fā)。
3. Filter(過濾器)對(duì)請(qǐng)求進(jìn)行攔截和修改掠剑、對(duì)上文的響應(yīng)進(jìn)行再處理
通常情況下屈芜,出于安全方面的考慮,服務(wù)端提供的服務(wù)往往都會(huì)有一定的校驗(yàn)邏輯(如:用戶登陸狀態(tài)校驗(yàn)、簽名校驗(yàn)等)井佑。在微服務(wù)架構(gòu)中属铁,系統(tǒng)由多個(gè)微服務(wù)組成,所有這些服務(wù)都需要這些校驗(yàn)邏輯躬翁,可以將這些校驗(yàn)邏輯寫到SpringCloudGateway的Filter過濾器中焦蘑。
Filter的分類
1. Pre 類型
在請(qǐng)求被轉(zhuǎn)發(fā)到微服務(wù)之前可以對(duì)請(qǐng)求進(jìn)行攔截和修改(如:參數(shù)校驗(yàn)、權(quán)限校驗(yàn)盒发、流量監(jiān)控例嘱、日志輸出以及協(xié)議轉(zhuǎn)換等操作)。
2. Post 類型
在微服務(wù)對(duì)請(qǐng)求做出響應(yīng)后可以對(duì)響應(yīng)進(jìn)行攔截和再處理(如:修改響應(yīng)內(nèi)容或響應(yīng)頭宁舰、日志輸出拼卵、流量監(jiān)控等)。
Filter的分類(按作用范圍)
1. GatewayFilter網(wǎng)關(guān)過濾器
作用于單個(gè)路由或者一組路由上的過濾器(對(duì)單個(gè)路由或者一組路由上傳入的請(qǐng)求和傳出響應(yīng)進(jìn)行攔截蛮艰,并實(shí)現(xiàn)一些與業(yè)務(wù)無關(guān)的功能腋腮,比如登陸狀態(tài)校驗(yàn)、簽名校驗(yàn)壤蚜、權(quán)限校驗(yàn)即寡、日志輸出、流量監(jiān)控等)袜刷。
GatewayFilter在配置文件中的寫法與Predicate類似聪富,格式如下:
/*
spring:
cloud:
gateway:
routes:
- id: xxxx
uri: xxxx
predicates:
- Path=xxxx
filters:
- AddRequestParameter=X-Request-Id,1024 #過濾器工廠會(huì)在匹配的請(qǐng)求頭加上一對(duì)請(qǐng)求頭,名稱為 X-Request-Id 值為 1024
- PrefixPath=/user #在請(qǐng)求路徑前面加上 /user
……
*/
2. GlobalFilter
作用于所有的路由上的全局過濾器水泉。
可以實(shí)現(xiàn)一些統(tǒng)一化的業(yè)務(wù)功能(如:權(quán)限認(rèn)證善涨、IP 訪問限制等)。當(dāng)某個(gè)請(qǐng)求被路由匹配時(shí)草则,那么所有的 GlobalFilter 會(huì)和該路由自身配置的 GatewayFilter 組合成一個(gè)過濾器鏈钢拧。
提供了多種默認(rèn)的GlobalFilter(如:與轉(zhuǎn)發(fā)、路由炕横、負(fù)載均衡等相關(guān)的全局過濾器)源内。但在實(shí)際的項(xiàng)目開發(fā)中,通常都會(huì)自定義GlobalFilter全局過濾器以滿足自身業(yè)務(wù)需求份殿,很少直接使用默認(rèn)GlobalFilter膜钓。
動(dòng)態(tài)路由
默認(rèn)情況下,SpringCloudGateway會(huì)根據(jù)服務(wù)注冊(cè)中心(如:EurekaServer)中維護(hù)的服務(wù)列表卿嘲,以服務(wù)名(spring.application.name)作為路徑創(chuàng)建動(dòng)態(tài)路由進(jìn)行轉(zhuǎn)發(fā)颂斜,從而實(shí)現(xiàn)動(dòng)態(tài)路由功能。
可以在配置文件中拾枣,將Route的uri地址修改為以下形式:
lb://service-name
說明:
1. lb:uri的協(xié)議(表示開啟SpringCloudGateway的負(fù)載均衡功能)沃疮。
2. service-name:服務(wù)名(SpringCloudGateway會(huì)根據(jù)它獲取到具體的微服務(wù)地址)盒让。
特性:
1. 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 構(gòu)建司蔬。
2. 能夠在任意請(qǐng)求屬性上匹配路由邑茄。
3. predicates(斷言) 和 filters(過濾器)是特定于路由的。
4. 集成了 Hystrix 熔斷器俊啼。
5. 集成了 Spring Cloud DiscoveryClient(服務(wù)發(fā)現(xiàn)客戶端)肺缕。
6. 易于編寫斷言和過濾器。
7. 能夠限制請(qǐng)求頻率授帕。
8. 能夠重寫請(qǐng)求路徑同木。
工作流程:
1. 客戶端將請(qǐng)求發(fā)送到SpringCloudGateway上。
2. SpringCloudGateway通過 GatewayHandlerMapping 找到與請(qǐng)求相匹配的路由跛十,將其發(fā)送給 GatewayWebHandler泉手。
3. GatewayWebHandler通過指定的過濾器鏈(Filter Chain),將請(qǐng)求轉(zhuǎn)發(fā)到實(shí)際的服務(wù)節(jié)點(diǎn)中偶器,執(zhí)行業(yè)務(wù)邏輯返回響應(yīng)結(jié)果。
4. 過濾器之間用虛線分開是因?yàn)檫^濾器可能會(huì)在轉(zhuǎn)發(fā)請(qǐng)求之前(pre)或之后(post)執(zhí)行業(yè)務(wù)邏輯缝裤。
5. 過濾器(Filter)可以在請(qǐng)求被轉(zhuǎn)發(fā)到服務(wù)端前屏轰,對(duì)請(qǐng)求進(jìn)行攔截和修改(如:參數(shù)校驗(yàn)、權(quán)限校驗(yàn)憋飞、流量監(jiān)控霎苗、日志輸出以及協(xié)議轉(zhuǎn)換等)。
6. 過濾器可以在響應(yīng)返回客戶端之前榛做,對(duì)響應(yīng)進(jìn)行攔截和再處理(如:修改響應(yīng)內(nèi)容或響應(yīng)頭唁盏、日志輸出、流量監(jiān)控等)检眯。
7. 響應(yīng)原路返回給客戶端厘擂。
斷言 | 示例 | 說明 |
---|---|---|
Path | - Path=/user/list/** | 請(qǐng)求路徑必須與 /user/list/** 匹配。 |
Before | - Before=2001-10-20T11:47:34.255+08:00[Asia/Shanghai] | 必須是 2001 年 10 月 20 日 11 時(shí) 47 分 34.255 秒之前的請(qǐng)求。 |
After | - After=2001-10-20T11:47:34.255+08:00[Asia/Shanghai] | 必須是 2001 年 10 月 20 日 11 時(shí) 47 分 34.255 秒之后的請(qǐng)求。 |
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 時(shí) 18 分 33.226 秒 到 2001 年 10 月 20 日 15 時(shí) 23 分 33.226 秒之間的請(qǐng)求踩娘。 |
Cookie | - Cookie=name,com.sst.cx | 必須攜帶 Cookie 且 Cookie 的內(nèi)容為 name=com.sst.cx 的請(qǐng)求喳挑。 |
Header | - Header=X-Request-Id,\d+ | 請(qǐng)求頭上必須攜帶屬性 X-Request-Id 且屬性值為整數(shù)的請(qǐng)求。 |
Method | - Method=GET | 必須是GET請(qǐng)求屋匕。 |
路由過濾器 | 描述 | 參數(shù) | 使用示例 |
---|---|---|---|
AddRequestHeader | 攔截請(qǐng)求 并添加一個(gè)指定的請(qǐng)求頭參數(shù)。 | name:需要添加的請(qǐng)求頭參數(shù)的 key;value:需要添加的請(qǐng)求頭參數(shù)的value倒脓。 | - AddRequestHeader=my-request-header,1024 |
AddRequestParameter | 攔截請(qǐng)求 并添加一個(gè)指定的請(qǐng)求參數(shù)。 | name:需要添加的請(qǐng)求參數(shù)的 key含思;value:需要添加的請(qǐng)求參數(shù)的 value崎弃。 | - AddRequestParameter=my-request-param,c.biancheng.net |
AddResponseHeader | 攔截響應(yīng) 并添加一個(gè)指定的響應(yīng)頭參數(shù)甘晤。 | name:需要添加的響應(yīng)頭的 key;value:需要添加的響應(yīng)頭的 value吊履。 | - AddResponseHeader=my-response-header,c.biancheng.net |
PrefixPath | 攔截請(qǐng)求 并在請(qǐng)求路徑前增加一個(gè)指定的前綴安皱。 | prefix:需要增加的路徑前綴。 | - PrefixPath=/consumer |
PreserveHostHeader | 轉(zhuǎn)發(fā)請(qǐng)求時(shí)艇炎,保持客戶端的Host信息不變酌伊,然后將它傳遞到提供具體服務(wù)的微服務(wù)中。 | 無 | - PreserveHostHeader |
RemoveRequestHeader | 移除請(qǐng)求頭中指定的參數(shù)缀踪。 | name:需要移除的請(qǐng)求頭的 key居砖。 | - RemoveRequestHeader=my-request-header |
RemoveResponseHeader | 移除響應(yīng)頭中指定的參數(shù)。 | name:需要移除的響應(yīng)頭驴娃。 | - RemoveResponseHeader=my-response-header |
RemoveRequestParameter | 移除指定的請(qǐng)求參數(shù)奏候。 | name:需要移除的請(qǐng)求參數(shù)。 | - RemoveRequestParameter=my-request-param |
RequestSize | 配置請(qǐng)求體的大小唇敞,當(dāng)請(qǐng)求體過大時(shí)蔗草,將會(huì)返回 413 Payload Too Large。 | maxSize:請(qǐng)求體的大小疆柔。 | - name: RequestSize args: maxSize: 5000000 |
示例(Predicate的使用)
創(chuàng)建spring-cloud-gateway-9527子項(xiàng)目
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)關(guān)服務(wù)中不能引入spring-boot-starter-web依賴咒精,否則會(huì)報(bào)錯(cuò) -->
<!-- SpringCloudGateway網(wǎng)關(guān)依賴-->
<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 #端口號(hào)
spring:
application:
name: microServiceCloudGateway
cloud:
gateway: #網(wǎng)關(guān)路由配置
routes:
#將spring-cloud-provider-user-8001提供的服務(wù)隱藏起來,不暴露給客戶端旷档,只給客戶端暴露API網(wǎng)關(guān)的地址9527
- id: provider_user_list_routh #路由id,沒有固定規(guī)則模叙,但必須唯一,建議與服務(wù)名對(duì)應(yīng)
uri: http://localhost:8001 #匹配后提供服務(wù)的路由地址
predicates:
#斷言條件(必選全部符合)
- Path=/user/list/** #路徑匹配(注意:Path 中 P 為大寫)
- Method=GET #僅GET請(qǐng)求
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. 依次啟動(dòng)Eureka服務(wù)注冊(cè)中心集群鞋屈、spring-cloud-provider-user-8001范咨、spring-cloud-gateway-9527,
在瀏覽器訪問http://localhost:9527/user/list
示例(動(dòng)態(tài)路由)
1. 修改pom.xml文件:
server:
port: 9527 #端口號(hào)
spring:
application:
name: microServiceCloudGateway #服務(wù)注冊(cè)中心注冊(cè)的服務(wù)名
cloud:
gateway: #網(wǎng)關(guān)路由配置
discovery:
locator:
enabled: true #默認(rèn)值為 true厂庇,即默認(rèn)開啟從注冊(cè)中心動(dòng)態(tài)創(chuàng)建路由的功能渠啊,利用微服務(wù)名進(jìn)行路由
routes:
#將spring-cloud-provider-user-8001提供的服務(wù)隱藏起來,不暴露給客戶端权旷,只給客戶端暴露API網(wǎng)關(guān)的地址9527
- id: provider_user_list_routh #路由id,沒有固定規(guī)則昭抒,但唯一,建議與服務(wù)名對(duì)應(yīng)
uri: lb://springCloudProviderUser #動(dòng)態(tài)路由炼杖,使用服務(wù)名代替上面的具體帶端口 http://www.eureka7001.com:9527/user/list
predicates:
#斷言條件(必選全部符合)
- Path=/user/list/** #路徑匹配 注意:Path 中 P 為大寫
- Method=GET #只能時(shí) GET 請(qǐng)求時(shí)灭返,才能訪問
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. 重新啟動(dòng)spring-cloud-gateway-9527,
在瀏覽器中訪問http://localhost:9527/user/list
示例(GatewayFilter網(wǎng)關(guān)過濾器)
1. 修改pom.xml文件
predicates:
- Path=/get/**
filters:
- PrefixPath=/user #在請(qǐng)求路徑上增加一個(gè)前綴 /user
2. 重新啟動(dòng)spring-cloud-gateway-9527坤邪,
在瀏覽器中訪問http://localhost:9527/get/1
示例(GlobalFilter全局過濾器)
1. 創(chuàng)建MyGlobalFilter.java(自定義全局網(wǎng)關(guān)過濾器類)
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)關(guān)過濾器(GlobalFilter)
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("進(jìn)入自定義的全局過濾器 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 表示第一個(gè)
return 0;
}
}
2. 重新啟動(dòng)spring-cloud-gateway-9527艇纺,
在瀏覽器訪問http://localhost:9527/user/list?uname=123456怎静,不添加uname參數(shù)會(huì)被過濾掉邮弹。
3. SpringCloud Config(分布式配置組件)
在分布式微服務(wù)系統(tǒng)中,幾乎所有服務(wù)的運(yùn)行都離不開配置文件的支持蚓聘,這些配置文件通常由各個(gè)服務(wù)自行管理腌乡,以properties或yml 格式(如:application.properties、application.yml)保存在各個(gè)微服務(wù)的類路徑下夜牡。
這種將配置文件散落在各個(gè)服務(wù)中的管理方式与纽,存在以下問題:
1. 管理難度大:配置文件散落在各個(gè)微服務(wù)中,難以管理塘装。
2. 安全性低:配置跟隨源代碼保存在代碼庫(kù)中急迂,容易造成配置泄漏。
3. 時(shí)效性差:微服務(wù)中的配置修改后蹦肴,必須重啟服務(wù)僚碎,否則無法生效。
4. 局限性明顯:無法支持動(dòng)態(tài)調(diào)整(如:日志開關(guān)阴幌、功能開關(guān))勺阐。
使用配置中心(如:百度的Disconf、淘寶的diamond矛双、360的QConf皆看、攜程的Apollo、SpringCloud的Config)對(duì)配置進(jìn)行統(tǒng)一管理可解決這些問題背零。
SpringCloudConfig
為微服務(wù)架構(gòu)中各個(gè)微服務(wù)提供集中化的外部配置支持。即:將各個(gè)微服務(wù)的配置文件集中存儲(chǔ)在一個(gè)外部的存儲(chǔ)倉(cāng)庫(kù)或系統(tǒng)(如:Git无埃、SVN等)中徙瓶,對(duì)配置統(tǒng)一管理。
分為2部分:
1. ConfigServer(分布式配置中心)嫉称,它是一個(gè)獨(dú)立運(yùn)行的微服務(wù)應(yīng)用侦镇,用來連接配置倉(cāng)庫(kù)并為客戶端提供獲取配置信息、加密信息和解密信息的訪問接口织阅。
2. ConfigClient:微服務(wù)架構(gòu)中的各個(gè)微服務(wù)壳繁,它們通過ConfigServer對(duì)配置進(jìn)行管理,并從ConfigSever中獲取和加載配置信息荔棉。
Spring Cloud Config 默認(rèn)使用 Git 存儲(chǔ)配置信息闹炉,因此使用 Spirng Cloud Config 構(gòu)建的配置服務(wù)器天然就支持對(duì)微服務(wù)配置的版本管理。我們可以使用 Git 客戶端工具方便地對(duì)配置內(nèi)容進(jìn)行管理和訪問润樱。除了 Git 外渣触,Spring Cloud Config 還提供了對(duì)其他存儲(chǔ)方式的支持,例如 SVN壹若、本地化文件系統(tǒng)等嗅钻。
工作流程:
1. 開發(fā)或運(yùn)維人員提交配置文件到遠(yuǎn)程的 Git 倉(cāng)庫(kù)皂冰。
2. Config 服務(wù)端(分布式配置中心)負(fù)責(zé)連接配置倉(cāng)庫(kù) Git,并對(duì) Config 客戶端暴露獲取配置的接口养篓。
3. Config 客戶端通過 Config 服務(wù)端暴露出來的接口秃流,拉取配置倉(cāng)庫(kù)中的配置。
4. Config 客戶端獲取到配置信息柳弄,以支持服務(wù)的運(yùn)行舶胀。
特點(diǎn):
1. 與Spring的生態(tài)體系無縫集成。
2. 將所有微服務(wù)的配置文件集中存儲(chǔ)在一個(gè)外部的存儲(chǔ)倉(cāng)庫(kù)或系統(tǒng)中統(tǒng)一管理语御。
3. Spring Cloud Config 配置中心將配置以 REST 接口的形式暴露給各個(gè)微服務(wù)峻贮,以方便各個(gè)微服務(wù)獲取。
4. 微服務(wù)可以通過 Spring Cloud Config 向配置中心統(tǒng)一拉取屬于它們自己的配置信息应闯。
5. 當(dāng)配置發(fā)生變化時(shí)纤控,微服務(wù)不需要重啟即可感知到配置的變化,并自動(dòng)獲取和應(yīng)用最新配置碉纺。
6. 一個(gè)應(yīng)用可能有多個(gè)環(huán)境船万,例如開發(fā)(dev)環(huán)境、測(cè)試(test)環(huán)境骨田、生產(chǎn)(prod)環(huán)境等等耿导,開發(fā)人員可以通過 Spring Cloud Config 對(duì)不同環(huán)境的各配置進(jìn)行管理,且能夠確保應(yīng)用在環(huán)境遷移后仍然有完整的配置支持其正常運(yùn)行态贤。
配置文件的訪問規(guī)則(在瀏覽器中可直接對(duì)配置文件進(jìn)行訪問)
格式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}:應(yīng)用名(即:配置文件的名稱)舱呻,如:config-dev。
2. {profile}:環(huán)境名悠汽,一個(gè)項(xiàng)目通常都有開發(fā)(dev)版本箱吕、測(cè)試(test)版本、生產(chǎn)(prod)版本柿冲,配置文件則以application-{profile}.yml 的形式進(jìn)行區(qū)分茬高,如:application-dev.yml、application-test.yml假抄、application-prod.yml等怎栽。
3. {label}:Git分支名,默認(rèn):master分支宿饱,當(dāng)訪問默認(rèn)分支下的配置文件時(shí)熏瞄,該參數(shù)可以省略,即第二種訪問方式谬以。
4. {suffix}:配置文件的后綴巴刻,如:config-dev.yml的后綴為yml。
示例(搭建Config服務(wù)端)
1. 在Github或Gitee上創(chuàng)建一個(gè)名為 springcloud-config 的倉(cāng)庫(kù)(Repository)蛉签,并獲取該倉(cāng)庫(kù)的地址胡陪。
2. 創(chuàng)建spring-cloud-config-center-3344子項(xiàng)目,修改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>
<!--配置中心服務(wù)器依賴-->
<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 #端口號(hào)
spring:
application:
name: spring-cloud-config-center #服務(wù)名
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/springcloud-config.git
#倉(cāng)庫(kù)名
search-paths:
- springcloud-config
force-pull: true
# 如果Git倉(cāng)庫(kù)為公開倉(cāng)庫(kù)沥寥,可以不填寫用戶名和密碼,如果是私有倉(cāng)庫(kù)需要填寫
username: ********
password: ********
#分支名
label: master
eureka:
client: #將客戶端注冊(cè)到 eureka 服務(wù)列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務(wù)注冊(cè)到 Eureka 集群
4. 給主啟動(dòng)類添加@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. 新建一個(gè)名為config-dev.yml文件邑雅,并將其上傳到 springcloud-config 倉(cāng)庫(kù) master 分支下。
config:
info: com.sst.cx
version: 1.0
6. 依次啟動(dòng)服務(wù)注冊(cè)中心集群和 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子項(xiàng)目
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)級(jí)別的淮野,加載優(yōu)先級(jí)高于 application.yml ,負(fù)責(zé)從外部加載配置并解析
server:
port: 3355 #端口號(hào)
spring:
application:
name: spring-cloud-config-client #服務(wù)名
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 服務(wù)端(配置中心)地址
eureka:
client: #將客戶端注冊(cè)到 eureka 服務(wù)列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務(wù)注冊(cè)到 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)容骤星,并展示到頁(yè)面
@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. 給主啟動(dòng)類添加@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. 啟動(dòng)服務(wù)注冊(cè)中心集群爆哑、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客戶端的情況下编丘,手動(dòng)刷新配置 來獲取最新配置)
從上例中可知,配置文件更新后
1. SpringCloudConfig服務(wù)端可以直接從Git倉(cāng)庫(kù)中獲取最新的配置彤悔。
2. SpringCloudConfig客戶端則需要重啟嘉抓,否則無法通過SpringCloudConfig服務(wù)端獲取最新的配置。
通過在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配置文件中添加(對(duì)外暴露SpringBootActuator的監(jiān)控節(jié)點(diǎn)):
# Spring Boot 2.50對(duì) actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點(diǎn)蕾盯,只暴露了 health 節(jié)點(diǎn)幕屹,本段配置(*)就是為了開啟所有的節(jié)點(diǎn)
management:
endpoints:
web:
exposure:
include: * # * 在yaml 文件屬于關(guān)鍵字,所以需要加引號(hào)
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了挫鸽。
這種方式帶來一個(gè)問題:只要配置倉(cāng)庫(kù)中的配置發(fā)生改變说敏,就需要挨個(gè)向Config客戶端手動(dòng)發(fā)送POST請(qǐng)求,通知它們重新拉取配置丢郊。
在微服務(wù)架構(gòu)中盔沫,一個(gè)系統(tǒng)往往包含十幾甚至幾十個(gè)服務(wù)医咨,如果因?yàn)槟骋粋€(gè)配置文件的修改而向幾十個(gè)微服務(wù)發(fā)送POST請(qǐng)求,這顯然是不合理的架诞。
SpringCloudBus(消息總線)
通過輕量級(jí)的消息代理(如:RabbitMQ拟淮、Kafka)(構(gòu)建一個(gè)公共的消息主題Topic,默認(rèn)為“springCloudBus”谴忧,這個(gè)Topic中的消息會(huì)被所有服務(wù)實(shí)例監(jiān)聽和消費(fèi)很泊。當(dāng)其中的一個(gè)服務(wù)刷新數(shù)據(jù)時(shí),SpringCloudBus會(huì)把信息保存到Topic中沾谓,這樣監(jiān)聽這個(gè)Topic的服務(wù)就收到消息并自動(dòng)消費(fèi))將微服務(wù)架構(gòu)中的各個(gè)服務(wù)連接起來委造,實(shí)現(xiàn)廣播狀態(tài)更改、事件推送等功能均驶,還可以實(shí)現(xiàn)微服務(wù)之間的通信功能昏兆。
Config+Bus實(shí)現(xiàn)配置的動(dòng)態(tài)刷新(一次通知,處處生效)
利用SpringCloudBus的特殊機(jī)制可以實(shí)現(xiàn)很多功能辣恋,如:配合SpringCloudConfig可以實(shí)現(xiàn)配置的動(dòng)態(tài)刷新亮垫。當(dāng)Git倉(cāng)庫(kù)中的配置發(fā)生了改變,只需要向某一個(gè)服務(wù)(既可以是Config服務(wù)端伟骨,也可以是Config客戶端)發(fā)送一個(gè) POST 請(qǐng)求饮潦,SpringCloudBus 就可以通過消息代理通知其他服務(wù)重新拉取最新配置,以實(shí)現(xiàn)配置的動(dòng)態(tài)刷新携狭。
工作流程:
1. 當(dāng) Git 倉(cāng)庫(kù)中的配置發(fā)生改變后继蜡,運(yùn)維人員向 Config 服務(wù)端發(fā)送一個(gè) POST 請(qǐng)求,請(qǐng)求路徑為“/actuator/refresh”逛腿。
2. Config 服務(wù)端接收到請(qǐng)求后稀并,會(huì)將該請(qǐng)求轉(zhuǎn)發(fā)給服務(wù)總線 Spring Cloud Bus。
3. Spring Cloud Bus 接到消息后单默,會(huì)通知給所有 Config 客戶端碘举。
4. Config 客戶端接收到通知,請(qǐng)求 Config 服務(wù)端拉取最新配置搁廓。
5. 所有 Config 客戶端都獲取到最新的配置引颈。
示例(SpringCloudBus動(dòng)態(tài)刷新配置)全局廣播
1. 在spring-cloud-config-center-3344的pom.xml文件中,添加
<!--添加消息總線(Bus)對(duì) 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 相關(guān)配置蝙场,15672 是web 管理界面的端口,5672 是 MQ 的訪問端口###########
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
# Spring Boot 2.50對(duì) actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點(diǎn)粱年,只暴露了 heath 節(jié)點(diǎn)售滤,本段配置(*)就是為了開啟所有的節(jié)點(diǎn)
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
3. 在spring-cloud-config-client-3355的pom.xml文件中,添加
<!--添加消息總線(Bus)對(duì) 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 相關(guān)配置完箩,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)級(jí)別的秃励,加載優(yōu)先級(jí)高于application.yml ,負(fù)責(zé)從外部加載配置并解析)
server:
port: 3366 #端口號(hào)為 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 相關(guān)配置夺鲜,15672 是web 管理界面的端口,5672 是 MQ 的訪問端口###########
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
###################### eureka 配置 ####################
eureka:
client: #將客戶端注冊(cè)到 eureka 服務(wù)列表內(nèi)
service-url:
defaultZone: http://www.eureka7001.com:7001/eureka/,http://www.eureka7002.com:7002/eureka/,http://www.eureka7003.com:7003/eureka/ #將服務(wù)注冊(cè)到 Eureka 集群
# Spring Boot 2.50對(duì) actuator 監(jiān)控屏蔽了大多數(shù)的節(jié)點(diǎn)呐舔,只暴露了 heath 節(jié)點(diǎn)币励,本段配置(*)就是為了開啟所有的節(jié)點(diǎn)
management:
endpoints:
web:
exposure:
include: "*" # * 在yaml 文件屬于關(guān)鍵字,所以需要加引號(hào)
6. 啟動(dòng)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
可能遇到的錯(cuò)誤:
1. 執(zhí)行curl命令405(Method Not Allowed)
原因:springcloud版本過高,降低版本剑辫,或者bus-refresh改為busrefresh
2. 執(zhí)行curl命令500(Internal Server Error)
【存疑:改為busrefresh后報(bào)500】
示例(SpringCloudBus動(dòng)態(tài)刷新配置)定點(diǎn)通知
定點(diǎn)通知:不再通知所有的Config客戶端干旧,而是根據(jù)需求只通知指定Config客戶端。
只要我們?cè)诎l(fā)送POST請(qǐng)求時(shí)使用以下格式即可:
http://{hostname}:{port}/actuator/bus-refresh/{destination}
說明:
1. {hostname}:表示Config服務(wù)端的主機(jī)地址(域名 或 IP地址)妹蔽。
2. {port}:表示Config服務(wù)端的端口號(hào)椎眯。
3. {destination}:表示需要定點(diǎn)通知的Config客戶端(微服務(wù))。由Config客戶端的服務(wù)名(spring.application.name)+冒號(hào)+端口號(hào)(server.port)組成胳岂。
例:curl -X POST "http://localhost:3344/actuator/bus-refresh/spring-cloud-config-client:3355"