閘道器之SpringCloudGateway高階應用
阿新 • • 發佈:2020-09-07
一、熔斷降級
1.1 為什麼要實現熔斷降級?
在分散式系統中,閘道器作為流量的入口,因此會有大量的請求進入閘道器,向其他服務發起呼叫,其他服務不可避免的會出現呼叫失敗(超時、異常),失敗時不能讓請求堆積在閘道器上,需要快速失敗並返回給客戶端,想要實現這個要求,就必須在閘道器上做熔斷、降級操作。
1.2 基於 hystrix 熔斷降級
- 新增依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- 配置
server: # 配置應用埠 port: 8080 spring: application: # 配置應用名稱 name: gateway cloud: nacos: discovery: # 註冊服務中心地址 server-addr: 192.168.205.10:8848 gateway: routes: - id: order-service uri: lb://order-service predicates: - Path=/order/** filters: # 配置 Hystrix - name: Hystrix args: name: fallbackCmdA # 降級呼叫 URI fallbackUri: forward:/fallbackA # 設定超時時間,單位:ms hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000
- 建立降級回撥方法
@RestController
public class FallbackController {
@GetMapping("/fallbackA")
public String fallbackA() {
return "服務暫時不可用";
}
}
- 啟動 OrderService 和 gateway 服務,並訪問 http://localhost:8080/order/create 返回:
訂單建立成功
- 關閉OrderService 訪問 http://localhost:8080/order/create
服務暫時不可用
證明熔斷降級已生效。
二、限流
2.1 為什麼需要限流?
- 防止大量的請求使伺服器過載,導致服務不可用
- 防止網路攻擊
2.2 常見的限流演算法
計數器演算法
在指定時間內對請求數做累計,當數量大於設定的值時,後續的請求都將被拒絕。當指定時間過去後,將計數重置為0,重新開始計數。
弊端:如果在單位時間1s內只能允許100個請求訪問,在前10ms已經通過了100個請求,那後面的990ms所有的請求都會被拒絕,這種現象稱為“突刺現象”。
漏桶演算法
漏桶演算法可以消除"突刺現象",漏桶演算法內部有一個容器,類似生活用到的漏斗,當請求進來時,相當於水倒入漏斗,然後從下端小口慢慢勻速的流出。不管上面流量多大,下面流出的速度始終保持不變。不管服務呼叫方多麼不穩定,通過漏桶演算法進行限流,每10毫秒處理一次請求。因為處理的速度是固定的,請求進來的速度是未知的,可能突然進來很多請求,沒來得及處理的請求就先放在桶裡,既然是個桶,肯定是有容量上限,如果桶滿了,那麼新進來的請求就丟棄
弊端:無法應對短時間的突發流量。
令牌桶演算法
在令牌桶演算法中,存在一個桶,用來存放固定數量的令牌。演算法中存在一種機制,以一定的速率往桶中放令牌。每次請求呼叫需要先獲取令牌,只有拿到令牌,才有機會繼續執行,否則選擇選擇等待可用的令牌、或者直接拒絕。放令牌這個動作是持續不斷的進行,如果桶中令牌數達到上限,就丟棄令牌,所以就存在這種情況,桶中一直有大量的可用令牌,這時進來的請求就可以直接拿到令牌執行,比如設定qps為100,那麼限流器初始化完成一秒後,桶中就已經有100個令牌了,這時服務還沒完全啟動好,等啟動完成對外提供服務時,該限流器可以抵擋瞬時的100個請求。所以,只有桶中沒有令牌時,請求才會進行等待,最後相當於以一定的速率執行。
2.3 Gateway 限流支援
在 Spring Cloud Gateway 中,有 Filter 過濾器,因此可以在 pre 型別的 Filter 中自行實現上述三種過濾器。但是限流作為閘道器最基本的功能,Spring Cloud Gateway 官方就提供了 RequestRateLimiterGatewayFilterFactory 這個類,適用在 Redis 內的通過執行 Lua 指令碼實現了令牌桶的方式。具體實現邏輯在 RequestRateLimiterGatewayFilterFactory 類中,lua 指令碼在如下圖所示的資料夾中:
2.4 例項
- 新增 redis 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
- 在配置檔案中配置
server:
# 配置應用埠
port: 8080
spring:
application:
# 配置應用名稱
name: gateway
cloud:
nacos:
discovery:
# 註冊服務中心地址
server-addr: 192.168.205.10:8848
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- name: RequestRateLimiter
args:
# 用於限流的鍵的解析器的 Bean 物件的名字,通過 SpEL 表示式從 Spring 容器中獲取
key-resolver: '#{@hostAddrKeyResolver}'
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 3
redis:
host: localhost
port: 6379
database: 0
- 自定義限流策略,通過實現 KeyResolver 介面
基於 hostAddress 限流:
public class HostAddrKeyResolver implements KeyResolver {
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
基於 URI 限流:
public class UriKeyResolver implements KeyResolver {
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getURI().getPath());
}
}
基於使用者 限流:
public class UserKeyResolver implements KeyResolver {
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
}
只允許一個策略生效,這裡我們採用 HostAddrKeyResolver :
@Bean
public HostAddrKeyResolver hostAddrKeyResolver() {
return new HostAddrKeyResolver();
}
- 啟動 OrderService 和 gateway 服務,通過 jmeter 併發訪問
可以看到請求部分成功,部分失敗。