閘道器簡介
大家都都知道在微服務架構中,一個系統會被拆分為很多個微服務。那麼作為客戶端要如何去呼叫這麼多的微服務呢?如果沒有閘道器的存在,我們只能在客戶端記錄每個微服務的地址,然後分別去用。
這樣架構會存在很多問題:
- 每個業務都會需要鑑權、限流、許可權校驗、跨域等邏輯,
如果每個業務都各自為戰,自己造輪子實現一遍
,會很蛋疼,完全可以抽出來,放到一個統一的地方去做。 - 如果業務量比較簡單的話, 這種方式前期不會有什麼問題,但隨著業務越來越複雜,比如淘寶、亞馬遜,開啟一個頁面可能會涉及到數百個微服務協同工作,
如果每一個微服務都分配一個域名的話,一方面客戶端程式碼會很難維護,涉及到數百個域名,另一方面是連線數的瓶頸
,想象一 下你開啟一個APP,透過抓包發現涉及到了數百個遠端呼叫,這在移動端下會顯得非常低效。 - 後期如果需要對微服務進行重構的話, 也會變的非常麻煩,需要客戶端配合你一起進行改造,比如商品服務,隨著業務變的越來越複雜,
後期需要進行拆分成多個微服務,這個時候對外提供的服務也需要拆分成多個
,同時需要客戶端配合你進行改造,非常蛋疼。
上面的問題可以藉助 API 閘道器來解決。
注重穩定性:
- 全域性性流控
- 日誌統計
- 防止 SQL 注入
- 防止 Web 攻擊
- 遮蔽工具掃描
- 黑白 IP 名單
- 證照/加解密處理
提供更好的服務
- 服務級別流控
- 服務降級與熔斷
- 路由與負載均衡、灰度策略
- 服務過濾、聚合與發現
- 許可權驗證與使用者等級策略
- 業務規則與引數校驗
- 多級快取策略
所謂的API閘道器,就是指系統的統一入口,它封裝了應用程式的內部結構,為客戶端提供統一服務,一 些與業務本身功能無關的公共邏輯可以在這裡實現,諸如認證、鑑權、監控、路由轉發等等。新增上API閘道器之後,系統的架構圖變成了如下所示:
一、什麼是 Spring Cloud Gateway
閘道器作為流量的入口,常用的功能包括路由轉發,許可權校驗,限流等。
Spring Cloud Gateway 是 Spring Cloud 官方推出的第二代閘道器框架,定位於取代 Netlix Zuul。相比Zuul 來說,Spring Cloud Gateway 提供更優秀的效能,更強大的有功能。
Spring Cloud Gateway 由 WebFlux + Netty + Reactor 實現的響應式的 API 閘道器。它不能在傳統的servlet 容器中工作,也不能構建成 war 包。
Spring Cloud Gateway 旨在為微服務架構提供一種簡單且有效的 API 路由的管理方式,並基於 Filter 的方式提供閘道器的基本功能,例如說安全認證、監控、限流等等。
其它閘道器元件:
在 SpringCloud 微服務體系中,有個很重要的元件就是閘道器,在 1.x 版本中都是採用的 Zuul 閘道器;但在2.x版本中,zuul 的升級一直跳票,SpringCloud 最後自 己研發了一個閘道器替代 Zuul,那就是SpringCloud Gateway
網上很多地方都說 Zuul 是阻塞的,Gateway是非阻塞的,這麼說是不嚴謹的,準確的講 zuul 1.x 是阻塞的,而在 2.x 的版本中,Zuul 也是基於 Netty,也是非阻塞的,如果一定要說效能,其實沒多大差距。
Spring Cloud Gateway 功能特徵
- 基於Spring Framework 5,Project Reactor 和 Spring Boot 2.0 進行構建;
- 動態路由:能夠匹配任何請求屬性;
- 支援路徑重寫;
- 整合Spring Cloud服務發現功能(Nacos、 Eruka);
- 可整合流控降級功能(Sentinel、 Hystrix);
- 可以對路由指定易於編寫的Predicate (斷言)和Filter (過濾器);
1.1、核心概念
路由(route)
路由是閘道器中最基礎的部分,路由資訊包括一個ID、一個目的URI、一組斷言工廠、一組 Filter 組成。如果斷言為真,則說明請求的 URL 和配置的路由匹配。斷言(predicates)
Java8 中的斷言函式,SpringCloud Gateway 中的斷言函式型別是 Spring5.0 框架中的ServerWebExchange。斷言函式允許開發者去定義匹 Http request 中的任何資訊,比如請求頭和引數等。過濾器(Filter)
SpringCloud Gateway 中的 filter 分為 Gateway Fller 和 Global Filter。Fiter 可以對請求和響應進行處理。
二、Spring Cloud Gateway 快速開始
2.1、環境搭建
2.1.1、引入依賴
<!--gateway 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.1.2、配置 application.yml 檔案
現有兩個服務,order(訂單)和stock(庫存)服務
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# 需要轉發的地址
uri: http://localhost:8020
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order-server/**
# http://localhost:8088/order-server/order/add 路由到
# http://localhost:8020/order-server/order/add
filters:
# 轉發之前,去掉第一層的路徑
# http://localhost:8020/order/add
- StripPrefix=1
#- id: stock_route
啟動閘道器服務,請求http://localhost:8088/order-server/order/add
地址,可以看到路由到了訂單服務,並且請求庫存服務成功。
2.2、整合 Nacos
現在是在配置檔案中寫死了轉發的路徑地址
2.2.1、引入依賴
<!--nacos 服務註冊與發現-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.2.2、編寫配置檔案
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.33.62:8847
username: nacos
password: nacos
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
uri: lb://order-server
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order-server/**
filters:
- StripPrefix=1
可以看到 Nacos 註冊中心的服務
此時請求介面http://localhost:8088/order-server/order/add
配置簡寫
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.33.62:8847
username: nacos
password: nacos
# gateway 配置
gateway:
discovery:
locator:
# 是否啟動自動識別 nacos 服務
enabled: true
需要透過 nacos 中的服務名進行訪問http://localhost:8088/order-server/order/add
三、Gateway 路由斷言工廠(Route Predicate Factories)配置
作用:當請求 gateway 時候,使用斷言對請求進行匹配,如果匹配成功就路由轉發,匹配失敗就返回 404。
3.1、內建路由斷言工廠
SpringCloud Gateway 包括許多內建的斷言工廠,所有這些斷言都與 HTTP 請求的不同屬性匹配。具體如下:
3.1.1、基於 Datetime 型別的斷言工廠
此型別的斷言,根據時間做判斷,主要有三個:
AfterRoutePredicateFactory:接收一個日期引數,判斷請求日期是否晚於指定日期
BeforeRoutePredicateFactory:接收一 個日期引數,判斷請求日期是否早於指定日期
BetweenRoutePredicateFactory:接收兩個日期引數,判斷請求日期是否在指定時間段內
時間型別為 ZonedDateTime.now()
- After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.33.62:8847
username: nacos
password: nacos
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
uri: lb://order-server
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order/**
- After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]
請求成功
修改時間為當前時間之前的時間後,訪問介面為 404
3.1.2、基於遠端地址的斷言工廠
RemoteAddrRoutePredicateFactory:接收一個 IP 地址段,判斷請求主機地址是否在地址段中。
- RemoteAddr=192.168.1.1/24
3.1.3、基於 Cookie 的斷言工廠
CookieRoutePredicateFactory:接收兩個引數,cookie 名字和一個正規表示式。判斷請求 cookie 是否具有給定名稱且值與正規表示式匹配
- Cookie=chocolate,ch.
3.1.4、基於 Header 的斷言工廠
HeaderRoutePredicateFactory:接收兩個引數,標題名稱和正規表示式。判斷請求 Header 是否具有給定名稱且值與正規表示式匹配。
設定請求頭中,X-Request-Id,必須為數字
- Header=X-Request-Id,\d+
使用 postman 進行測試
當請求頭中沒有X-Request-Id
或者值不為數字時,訪問 404
3.1.5、基於 Host 的斷言工廠
HostRoutePredicateFactory:接收一個引數,主機名模式。判斷請求的 Host 是否滿足匹配規則。
- Host=**.testhost.org
3.1.6、基於 Method 請求方法的斷言工廠
MethodRoutePredicateFactory:接收一個引數,判斷請求型別是否跟指定的型別匹配。
- Method=GET
當請求為 POST 請求時,404
3.1.7、基於 Path 請求路徑的斷言工廠
PathRoutePredicateFactory:接收一個引數,判斷請求的 URI 部分是否滿足路徑規則。
# 效果類似於 url 中 /foo/xxx
- Path=/foo/{segment}
3.1.8、基於 Query 請求引數的斷言工廠
QueryRoutePredicateFactory:接收兩個引數,請求 param 和正規表示式,判斷請求引數是否具有給定名稱且值與正規表示式匹配。
# 效果類似於 url 中 ?baz=xxx
- Query=name,ba.
3.1.9、基於路由權重的斷言工廠
WeightRoutePredicateFactory:接收一個[組名權重],然後對於同一個組內的路由按照權重轉發
routes:
- id: weight_route1
uri: host1
predicates:
- Path=/order/**
- weitht=group3,1
- id: weight_route2
uri: host2
predicates:
- Path=/order/**
- Weight=group3,9
3.2、自定義路由斷言工廠
自定義路由斷言工廠需要繼承 AbstractRoutePredicateFactory 類,重寫 apply 方法的邏輯。在 apply 方法中可以透過 exchange.getRequest() 拿到 ServerHttpRequest 物件,從而可以獲取到請求的引數、請求方式、請求頭等資訊。
注意:類命名必須以 RoutePredicateFactory 結尾
- 必須是 spring 元件 bean
- 類必須加上 RoutePredicateFactory 作為結尾
- 必須繼承 AbstractRoutePredicateFactory
- 必須宣告靜態內部類,宣告屬性來接收配置檔案中對應的斷言資訊
- 需要結合 shortcutFieldOrder 進行繫結
- 透過 apply 進行邏輯判斷,true 就是匹配成功,false 匹配失敗
實現效果為,當路由中有 hudu 字串時,透過
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if (config.getName().equals("hudu")) {
return true;
}
return false;
}
};
}
/**
* 用於接收配置檔案中 斷言的資訊
*/
@Validated
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
application.yml 配置
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.33.62:8847
username: nacos
password: nacos
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
uri: lb://order-server
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order/**
- CheckAuth=hudu
當 CheckAuth 不為 hudu 時,請求 404
四、過濾器工廠(GatewayFilter Factories)配置
Gateway 內建了很多的過濾器工廠,我們透過一些過濾器工廠可以進行一些業務邏輯處理器,比如新增剔除響應頭,新增去除引數等
4.1、內建過濾器
4.1.1、新增請求頭
spring:
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
uri: lb://order-server
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order/**
filters:
# 新增請求頭
- AddRequestHeader=X-Request-color,red
@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
return color;
}
4.1.2、新增請求引數
spring:
# gateway 配置
gateway:
# 路由規則
routes:
# 路由唯一標識,路由到訂單
- id: order_route
# lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
uri: lb://order-server
# 斷言規則,用於路由規則的匹配
predicates:
- Path=/order/**
filters:
# 新增請求引數
- AddRequestParameter=color,blue
@GetMapping("/parameter")
public String parameter(@RequestParam("color")String color) {
return color;
}
4.1.3、為匹配路由統一新增字首
filters:
# 新增請求引數
- PrefixPath=/order-server
效果等同於在遠處呼叫的服務上新增
server:
servlet:
context-path: order-server
請求介面http://localhost:8088/order/test
實際是轉發到http://localhost:8088/order-server/order/test
4.1.4、重定向
filters:
# 重定向
- RedirectTo=302,https://www.baidu.com
請求介面http://localhost:8088/order/test
4.2、自定義過濾器工廠(區域性過濾器)
繼承 AbstractNameValueGatewayFilterFactory 且我們的自定義名稱必須要以 GatewayFilterFactory 結尾並交給 spring 管理。
實現效果當請求介面中帶有?name=hudu
時,才能請求成功
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
private static final Log log = LogFactory
.getLog(CheckAuthGatewayFilterFactory.class);
public CheckAuthGatewayFilterFactory() {
super(CheckAuthGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("key","value");
}
@Override
public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
// 獲取 key value 值
String key = exchange.getRequest().getQueryParams().getFirst(config.getKey());
// 如果 key value 等於對應的值 成功
if (StringUtils.hasText(key)) {
if (config.getValue().equals(key)) {
return chain.filter(exchange);
}
}
// 否則訪問 404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
};
}
public static class Config {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
filters:
- CheckAuth=name,hudu
4.3、全域性過濾器(Global Filters)配置
區域性過濾器和全域性過濾器的區別:
區域性過濾器:只針對某一個路由進行過濾,需要在路由中進行配置
全域性過濾器:針對所有路由請求,一旦定義了就會投入使用
4.3.1、LoadBalancerClientFilter
LoadBalancerClientFilter 會檢視 exchange 的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一個 URI),如果該值的 scheme 是 lb,比如:lb://myserver,它會使用 SpringCloud 的 LoadBalancerClient 來將 myservice 解析成實際的 host 和 port,並替換掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的內容。
其實就是用來整合負載均衡 Ribbon 的
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://mall-order
predicates:
- Path=/order/**
4.3.2、自定義全域性過濾器
一般像授權,許可權認證,日誌記錄等,會使用全域性過濾器
@Component
public class MyGlobalFilter implements GlobalFilter {
Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
如果既定義了區域性過濾器,又定義了全域性過濾器,會先經過區域性過濾器,透過之後再經過全域性過濾器
五、請求日誌記錄&跨域處理
5.1、Reactor Netty 訪問日誌
要啟用 Reactor Netty 訪問日誌,需要設定-Dreactor.netty.http.server.accessLogEnabled=true
它必須是 Java 系統屬性,而不是 Spring Boot 屬性。
可以將日誌記錄為具有單獨的訪問日誌檔案。
logback.xml
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
六、Gateway 跨域配置(CORS Configuration)
透過 yml 配置方式
spring:
cloud:
gateway:
globalcors:
cors-configurations:
# 允許跨域訪問的資源
'[/**]':
# 跨域允許來源
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTOIN
透過 javaconfig 配置方式
@Configuration
public class SystemCorsFilter {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允許的 method
corsConfiguration.addAllowedMethod("*");
// 允許的來源
corsConfiguration.addAllowedOrigin("*");
// 允許的請求頭
corsConfiguration.addAllowedHeader("*");
//由於是透過 webflux 的方式,需要額外新增此配置
// 訪問的資源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
七、Gateway 整合 Sentinel 限流
閘道器作為內部系統外的一層屏障,對內起到一定的保護作用,限流便是其中之一。閘道器層的限流可以簡單地針對不同路由進行限流,也可針對業務的介面進行限流,或者根據介面的特徵分組限流。
- 新增依賴
<!--sentinel 整合 gateway-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel 依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 新增配置
# 配置 sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8858
7.1、控制檯實現方式
從1.6.0版本開始,Sentinel 提供了Spring Cloud Gateway 的適配模組,可以提供兩種資源維度的限流:
- route 維度:即在 Spring 配置檔案中配置的路由條目,資源名為對應的 routeld
- 自定義 API 維度:使用者可以利用 Sentinel 提供的 API 來自定義一些 API 分組
order-server 服務中提供如下服務
@RequestMapping("/hello")
public String hello() {
System.out.println("下單成功!");
return "Hello World ";
}
@RequestMapping("/flow")
public String flow() {
return "正常訪問";
}
@RequestMapping("/flowThread")
public String flowThread() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
return "正常訪問";
}
@RequestMapping("/get")
public String get() {
return "查詢訂單";
}
@RequestMapping("/err")
public String err() {
int i = 1 / 0;
return "hello";
}
@RequestMapping("/get/{id}")
public String getById(@PathVariable("id")Integer id) {
System.out.println("正常訪問");
return "正常訪問";
}
啟動服務,可以看到 sentinel 已經為當前的路由生成了一個資源,設定流控 QPS 為 2,當請求閘道器頻繁時,閘道器被流控了。
7.2、Gateway 整合 Sentinel 流控降級詳細配置
7.2.1、Burst size:突發請求額外允許數
當一秒請求三次以上時,才會進行限流
7.2.2、針對斷言工廠進行限流
精確:與設定的值相等
子串:模糊匹配
正則:正規表示式匹配
注意,localhost訪問無效,需要透過 127.0.0.1 訪問才行
針對請求頭中的 X-Request-Id 的值進行限流
其它配置可以自行測試。
7.2.3、根據詳細的地址分組流控
現在就可以根據 api 分組進行流控
7.2.4、降級
7.3、自定義降級返回資訊
7.3.1、透過 yml
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858
scg:
fallback:
mode: response
response-body: '{"code":403,"msg":"限流了"}'
7.3.2、透過 GatewayCallbackManager
@Configuration
public class GatewayConfig {
Logger log = LoggerFactory.getLogger(this.getClass());
@PostConstruct
public void init() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
// 列印異常
log.error(throwable.getMessage());
HashMap<String, String> map = new HashMap<>();
map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
if (throwable instanceof FlowException) {
map.put("msg", "介面限流了");
} else if (throwable instanceof DegradeException) {
map.put("msg", "服務降級了");
} else if (throwable instanceof ParamFlowException) {
map.put("msg", "熱點引數限流了");
} else if (throwable instanceof SystemBlockException) {
map.put("msg", "觸發系統保護規則了");
} else if (throwable instanceof AuthorityException) {
map.put("msg", "授權規則不透過");
}
// 自定義異常處理
return ServerResponse
// 響應狀態碼
.status(HttpStatus.OK)
// 響應型別
.contentType(MediaType.APPLICATION_JSON)
// 響應內容
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
7.4、程式碼實現方式(瞭解)
使用者可以透過 GatewayRuleManage.loadRules(rules)手動載入閘道器規則
GatewayConfiguration 中新增
@Configuration
public class GatewayConfig {
Logger log = LoggerFactory.getLogger(this.getClass());
@PostConstruct
public void doInit() {
// 初始化自定義的 API
initCustomizedApis();
// 初始化閘道器限流規則
initGatewayRules();
// 自定義限流異常處理器
initBlockRequestHandler();
}
private void initCustomizedApis() {
HashSet<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api = new ApiDefinition("user_service_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/user/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
HashSet<GatewayFlowRule> rules = new HashSet<>();
// resource:資源名稱,可以是閘道器中的 route 名稱,或者是自定義的 API 分組名稱
// count:限流閾值
// intervalSec:統計時間視窗,單位是 秒,預設是 1 秒
rules.add(new GatewayFlowRule("order_route")
.setCount(2)
.setIntervalSec(1));
rules.add(new GatewayFlowRule("user_service_api").setCount(2).setIntervalSec(1));
// 載入閘道器規則
GatewayRuleManager.loadRules(rules);
}
@PostConstruct
public void initBlockRequestHandler() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
// 列印異常
log.error(throwable.getMessage());
HashMap<String, String> map = new HashMap<>();
map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
if (throwable instanceof FlowException) {
map.put("msg", "介面限流了");
} else if (throwable instanceof DegradeException) {
map.put("msg", "服務降級了");
} else if (throwable instanceof ParamFlowException) {
map.put("msg", "熱點引數限流了");
} else if (throwable instanceof SystemBlockException) {
map.put("msg", "觸發系統保護規則了");
} else if (throwable instanceof AuthorityException) {
map.put("msg", "授權規則不透過");
}
// 自定義異常處理
return ServerResponse
// 響應狀態碼
.status(HttpStatus.OK)
// 響應型別
.contentType(MediaType.APPLICATION_JSON)
// 響應內容
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
八、Gateway 閘道器高可用
為了保證 Gateway 的高可用性,可以同時啟動多個 Gateway 例項進行負載,在 Gateway 的上游使用Nginx 或者 F5 進行負載轉發以達到高可用。
本作品採用《CC 協議》,轉載必須註明作者和本文連結