SpringCloud(四)GateWay閘道器

Odousang發表於2021-05-03

GateWay閘道器

概述簡介

Gateway是在 Spring生態系統之上構建的AP閘道器服務,基於 Spring5, Spring Boot2和 Project Reactor等技術。
Gateway旨在提供一種簡單而有效的方式來對API進行路由,以及提供一些強大的過濾器功能,例如:熔斷、限流、重試
官網地址:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/

SpringCloud Gateway是 Spring Cloud的個全新專案,基於 Spring5.0+ Spring Boot2.0和 Project Reactor等技術開發的閘道器,它旨在為微服務架構提供一種簡單有效的統一的API路由管理方式
SpringCloud Gateway作為 Spring Cloud生態系統中的閘道器,目標是替代zuul,在 Spring Cloud2.0以上版本中,沒有對新版本的zuul2.0以上最新高效能版本進行整合,仍然還是使用的zuul1.×非 Reactor模式的老版本。Spring Cloud Gateway使用的 Webflux中的 reactor-netty響應式程式設計元件,底層使用了 Netty通訊框架
Spring Cloud Gateway的目標提供統-的路由方式且基於 Filter 鏈的方式提供了閘道器基本的功能,例如:安全,監控/指標,和限流

主要特點

作用:反向代理,鑑權,流量控制,熔斷,日誌監控

  • 基於 Spring Framework5, Project Reactor和 Spring Boot2.0進行構建
  • 動態路由:能夠匹配任何請求屬性
  • 可以對路由指定 Predicate(斷言)和 Filter(過濾器)
  • 整合 Hystrix的斷路器功能
  • 整合 Spring Cloud服務發現功能
  • 請求限流功能
  • 支援路徑重寫

三大核心概念

Route(路由)
路由是構建閘道器的基本模組,它由 ID,目標URI,一系列的斷言和過濾器組成,如果斷言為 true 則匹配該路由
Predicate(斷言)
開發人員可以匹配HTTP請求中的所有內容(例如請求頭或請求引數),如果請求與斷言相匹配則進行路由
Filter(過濾)
指的是 Spring框架中 Gateway Filter的例項,使用過濾器,可以在請求被路由前或者之後對請求進行修改
web請求,通過一些匹配條件,定位到真正的服務節點。並在這個轉發過程的前後,進行一些精細化控制,predicate就是我們的匹配條件,而 Filter ,就可以理解為個無所不能的攔截器有了這兩個元素,再加上目標 uri 就可以實現一個具體的路由了

客戶端向 Spring Cloud Gateway 發出請求,然後在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其傳送到 Gateway Web handler,Handler再通過指定的過濾器鏈來將請求傳送到我們實際的服務執行業務邏輯,然後返回,過濾器之間用虛線分開是因為過濾器可能會在傳送代理請求之前("pre")或之後("post")執行業務邏輯
Filter在"pre"型別的過濾器可以做引數校驗、許可權校驗、流量監控、日誌輸岀、協議轉換等,在"post”型別的過濾器中可以做響應內容、響應頭的修改,日誌的輸出,流量監控等有著非常重要的作用

路由配置

  1. 建立一個新的 module
  2. 匯入 GateWay 依賴
<!-- Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.2.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  1. 配置路由對映
  • 第一種配置方式:在配置檔案 yml 中配置
server:
  port: 9527

spring:
  application:
    name: GateWay-Service
  cloud:
    gateway:
      routes:
        # 路由的 ID,沒有固定規則但要求唯一,建議配合服務名
        - id: payment-route1
        # 匹配後提供服務的路由地址
          uri: http://localhost:8001
        # 斷言,路徑相匹配的進行路由
        # 配置服務端的方法路徑
          predicates:
            - Path=/payment/query/**

        - id: payment-route2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/discovery/**

# Eureka
eureka:
  client:
    # 表示是否將自己註冊到 EurekaServer
    register-with-eureka: true
    # 是否從 EurekaServer 抓取已有的註冊資訊
    # 單節點無所謂,叢集必須設定為 true 才能配合 ribbon 使用
    fetch-registry: true
    service-url:
      # 單機版
      # defaultZone: http://localhost:7001/eureka/
      # 叢集版
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
  instance:
    hostname: GateWay-Service

  • 第二種配置方式:自定義配置檔案
@Configuration
public class GateConfig {

    /**
     * 測試通過閘道器 跳轉到 百度新聞 的頁面 http://news.baidu.com/guonei
     * 配置了一個 id 為 payment-route3 的路由規則
     * 當訪問地址為 http://localhost:9527/guonei 會自動轉發到 http://news.baidu.com/guonei
     * @param routeLocatorBuilder
     * @return
     */
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder){
        return routeLocatorBuilder.routes()
                .route( "payment-route3",r -> {
                    return r.path("/guonei")
                            .uri("http://news.baidu.com/guonei");
                })
                .build();
    }
}

動態路由

預設情況下 Gateway會根據註冊中心註冊的服務列表,以註冊中心上微服名為路徑建立動態路由進行轉發,從而實現動態路由的功能
配置 yml 配置檔案
需要注意的是 uri 的協議為lb,表示啟用 Gateway的負載均衡功能
lb://service Name 是spring cloud gateway在微服務中自動為我們建立的負載均衡uri

spring:
  application:
    name: GateWay-Service
  cloud:
    gateway:
      discovery:
        locator:
          # 開啟從註冊中心動態建立路由的功能,利用微服務名進行路由
          enabled: true
      routes:
        # 路由的 ID,沒有固定規則但要求唯一,建議配合服務名
        - id: payment-route1
        # 匹配後提供服務的路由地址
        # uri: http://localhost:8001 不能寫死
        # 匹配 服務端 的路由地址,就是微服務名
          uri: lb://provider-payment-service
        # 斷言,路徑相匹配的進行路由
        # 配置服務端的方法路徑
          predicates:
            - Path=/payment/lb/**

        - id: payment-route2
          uri: lb://provider-payment-service
          predicates:
            - Path=/payment/query/**

開啟兩個服務端8001和8002
實現了動態路由,可以測試到此時埠可以互相切換

Predicate使用

當開啟路由閘道器時

SpringCloud Gateway 將路由匹配作為 Spring WebFlux HandlerMapping基礎架構的部分
SpringCloud Gateway 包括許多內建的 RoutePredicate 工廠。所有這些Predicate都與HTTP請求的不同屬性匹配,多個Route Predicate工廠可以進行組合
Spring Cloud Gateway建立 Route物件時,使用 RoutePredicate Factory建立 Predicate物件, Predicate物件可以賦值給Route. Spring Cloud Gateway包含許多內建的 Route predicate factories
所有這些謂詞都匹配HTTP請求的不同屬性。多種謂詞工廠可以組合,並通過邏輯and組合

常用的 Route Predicate
After

路由規則可以匹配一個時間,設定在{arg}之後,當請求的時間在配置時間之後,才會交給 route 去處理

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2021-01-20T17:42:47.789-07:00[China/Shanghai]
Before

路由規則可以匹配一個時間,設定在{arg}之前,當請求的時間在配置時間之前,才會交給 route 去處理

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Before=2021-01-20T17:42:47.789-07:00[China/Shanghai]
Between

路由規則可以匹配一個時間,設定在{arg}之間,當請求的時間在配置時間之間,才會交給 route 去處理

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2021-01-20T17:42:47.789-07:00[China/Shanghai], 2021-01-21T17:42:47.789-07:00[China/Shanghai]

需要2個引數,一個是cookie名字,另一個是值,可以為正規表示式。它用於匹配請求中,帶有該名稱的cookie和cookie匹配正規表示式的請求

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=name, zhangsan

# 例
# 請求帶有cookie名為name, cookie值為 zhangsan 的請求
# 將都會轉發到uri為 https://example.org 的地址上

# curl 請求
# $ curl -H ‘Cookie:name=zhangsan’ localhost:8081
  • 不帶cookie訪問

  • 帶cookie訪問

在上面的配置中,當請求的 Header 中有 X-Request-Id的header名,且header值為數字時,請求會被路由到配置的 uri

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

# 例
# $ curl -H ‘X-Request-Id:1’ localhost:8081

Host

需要一個引數即hostname,它可以使用 . 或者 * 等去匹配host。這個引數會匹配請求頭中的host的值,一致,則請求正確轉發

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

# 如果請求的主機標頭的值為 
    www.somehost.org 
    或 beta.somehost.org
    或 www.anotherhost.org
# 則此路由匹配
# curl 請求
# curl -H ‘Host:www.adou.com’ localhost:8081

Method

如果請求方法是GET或POST,則此路由匹配

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST
Path

當請求路徑滿足 /red/** ,則會經過 route 到達 https://example.org

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/**
Query

也可以只填一個引數,填一個引數時,則只匹配引數名,當請求中請求引數包含 green,則會經過 route 到達 https://example.org

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green
        - Query=color, green
# 配置了請求中含有引數color,並且color的值匹配green.,則請求命中路由
# curl 請求
# $ curl localhost:8081?color=green
RemoterAddr

請求遠端地址,則會經過 route

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

# 如果請求的遠端地址是 192.168.1.10 ,則此路由匹配
Weight

權重採用兩個引數匹配 group 和 weight,當達成條件則會路由轉發

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

# 這條路線會將大約80%的流量轉發到 weighthigh.org
# 將大約20%的流量轉發到 weightlow.org

說白了, Predicate就是為了實現一組匹配規則,讓請求過來找到對應的 Route進行處理

Filter使用

路由過濾器可用於修改進入的HTTP請求和返回的HTTP響應,路由過濾器只能指定路由進行使用
Spring Cloud Gateway內建了多種路由過濾器,他們都由 GatewayFilter 的工廠類來產生
種類:GateWayFilterGlobalFilter

自定義過濾器
自定義全域性GlobalFilter
主要繼承兩個介面:GlobalFilter,Ordered
作用:全域性日誌記錄,統一閘道器鑑權等等

@Component
@Slf4j
public class LogGateWayFilter 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_FOUND);
            return exchange.getResponse().setComplete();
        }
        // 返回
        log.info("進入攔截器");
        return chain.filter(exchange);
    }

    /**
     * 載入過濾器的順序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

相關文章