SpringCloud-Gateway 閘道器路由、斷言、過濾

農夫三拳有點疼~發表於2020-04-25

Gateway 簡介

是什麼?

Spring Cloud 全家桶中有個很重要的元件:閘道器。在 1.x 版本中使用的是 Zuul 閘道器,但是到了 2.x,由於Zuul的升級不斷跳票,Spring Cloud 自己研發了一套閘道器元件:Spring Cloud Gateway。

Spring Cloud Gateway基於 Spring Boot 2.x,Spring WebFlux 和 Project Reactor 構建,使用了 Webflux 中的 reactor-netty 響應式程式設計元件,底層使用了 Netty 通訊框架。

詳見:官網

能幹嘛?

反向代理

鑑權

流量控制

熔斷

日誌監控

......

閘道器在微服務架構中的位置

Gateway 的三大概念

Route(路由):路由是構建閘道器的基本模組,它由 ID、目標 URI、一系列的斷言和過濾器組成,如果斷言為 true 則匹配該路由

Predicate(斷言)參考的是 Java8 中的 java.util.function.Predicate。開發人員可以匹配 HTTP 請求中的所有內容(例如請求頭或請求引數),如果請求與斷言相匹配則進行路由

Filter(過濾):指的是 Spring 框架中 GatewayFilter 的例項,使用過濾器,可以在請求被路由之前或之後對請求進行修改

工作流程

Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line is that filters can run logic both before and after the proxy request is sent. All “pre” filter logic is executed. Then the proxy request is made. After the proxy request is made, the “post” filter logic is run.

翻譯:客戶端向 Spring Cloud Gateway 發出請求。如果閘道器處理程式對映確定請求與路由匹配,則將其傳送到閘道器 Web 處理程式。該處理程式通過特定於請求的過濾器鏈來執行請求。 篩選器由虛線分隔的原因是,篩選器可以在傳送代理請求之前和之後執行邏輯。所有 “前置“ 過濾器邏輯均被執行,然後發出代理請求,發出代理請求後,將執行“ 後置 ”過濾器邏輯。

總結:路由轉發 + 執行過濾器鏈

兩種配置方式

配置檔案方式

我們以訪問「百度新聞網」為例,新增如下配置

server:
  port: 9527
spring:
  application:
    name: cloud-gateway9527
  cloud:
    gateway:
      routes:
        - id: news						# 路由id
          uri: http://news.baidu.com	# 真實呼叫地址
          predicates:
            - Path=/guonei				# 斷言,符合規則進行路由

瀏覽器雖然輸入 localhost:9527/guonei,卻會轉發到指定的地址

編碼方式

新增配置檔案

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("news2", r -> r.path("/guoji").uri("http://news.baidu.com"))
                .build();
    }
}

效果:

動態路由

開啟後,預設情況下 Gateway 會根據註冊中心註冊的服務列表,以註冊中心上微服務名為路徑建立動態路由進行轉發,從而實現動態路由的功能

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #開啟從註冊中心動態建立路由的功能,利用微服務名進行路由
      routes:
        - id: payment_routh1
          #uri: http://localhost:8001     #靜態,寫死了地址,只能呼叫一個服務
          uri: lb://CLOUD-PAYMENT-SERVICE #動態,lb://微服務名
          predicates:
            - Path=/payment/get/**
        - id: payment_routh2
          #uri: http://localhost:8001
          uri: lb://CLOUD-PAYMENT-SERVICE
          predicates:
            - Path=/payment/lb/**

Predicate 的使用

時間相關配置

After:在指定時間之進行路由

Before:在指定時間之進行路由

Between:在指定時間之進行路由

predicates:
    - Path=/payment/lb/**
    #- After=2020-04-25T16:30:58.215+08:00[Asia/Shanghai]
    #- Before=2020-04-25T16:40:58.215+08:00[Asia/Shanghai]
    - Between=2020-04-25T16:35:58.215+08:00[Asia/Shanghai],2020-04-25T16:40:58.215+08:00[Asia/Shanghai]

上述配置的時間格式可以通過以下程式碼得到

@Test
public void test(){
    ZonedDateTime now = ZonedDateTime.now();
    System.out.println(now);
}

請求相關配置

Cookie

配置說明:【Cookie=cookie名, cookie值的正規表示式規則】

predicates:
  - Path=/payment/lb/**
  - Cookie=id, [0-9]

使用 curl 工具模擬攜帶 cookie 傳送請求

Header

配置說明:【Header=header名, header值的正規表示式規則】

predicates:
  - Path=/payment/lb/**
  - Header=h, [a-h]

Host

配置說明:【Host=主機名(可配置多個,也可以使用萬用字元)】

predicates:
  - Path=/payment/lb/**
  - Host=**.a.com,**.b.cn

Method

配置說明:【Method=請求型別】

predicates:
  - Path=/payment/lb/**
  - Method=GET

Path

配置說明:【Path=請求路徑】

predicates:
  - Path=/payment/lb/**

Query

配置說明:【Query=引數名,引數值】

predicates:
  - Path=/payment/lb/**
  - Query=name, zhangsan

詳見:官網

Filter 的使用

  • 生命週期:pre、post
  • 種類:GatewayFilter、GlobalFilter

GatewayFilter 在官方文件有幾十種!詳細配置可參考 官網,這裡主要介紹自定義全域性過濾器。

@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        //使用者名稱為空時,給出錯誤響應
        if (username == null) {
            log.info("使用者名稱為空,非法登入");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}


獲取完整程式碼

相關文章