1. 程式人生 > 實用技巧 >閘道器之SpringCloudGateway高階應用

閘道器之SpringCloudGateway高階應用

一、熔斷降級

1.1 為什麼要實現熔斷降級?

在分散式系統中,閘道器作為流量的入口,因此會有大量的請求進入閘道器,向其他服務發起呼叫,其他服務不可避免的會出現呼叫失敗(超時、異常),失敗時不能讓請求堆積在閘道器上,需要快速失敗並返回給客戶端,想要實現這個要求,就必須在閘道器上做熔斷、降級操作。

1.2 基於 hystrix 熔斷降級

  1. 新增依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 配置
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
  1. 建立降級回撥方法
@RestController
public class FallbackController {

    @GetMapping("/fallbackA")
    public String fallbackA() {
        return "服務暫時不可用";
    }
}

  1. 啟動 OrderServicegateway 服務,並訪問 http://localhost:8080/order/create 返回:
訂單建立成功
  1. 關閉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 例項

  1. 新增 redis 依賴
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
  1. 在配置檔案中配置
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
  1. 自定義限流策略,通過實現 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();
}
  1. 啟動 OrderServicegateway 服務,通過 jmeter 併發訪問

可以看到請求部分成功,部分失敗。