SpringCloud學習筆記【十一】服務網關Gateway

SpringCloud Gateway概述

是什麼?

Spring Cloud Gateway is an intelligent and programmable router based on Project Reactor.

官網:https://spring.io/projects/spring-cloud-gateway

在雲架構中運行着衆多客戶端和服務端,API網關的存在提供了保護和路由消息,隱藏服務,限制負載等等功能。

Spring Cloud Gateway提供了一個在Spring生態系統之上構建的API網關,包括:Spring 5,Spring Boot 2和Project Reactor。 Spring Cloud Gateway旨在提供一種簡單而有效的方法來對API進行路由,提供精準控制。

爲了提升網關的性能,Spring Cloud Gateway是基於WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通信框架Netty

SpringCloud Gateway的目標是:提供統一的路由方式且基於FilterChain的方式提供網關基本的功能,例如:安全,監控/指標,彈性,限流等等。

有哪些特性?

  • 基於SpringFramework5,ProjectReactor和SpringBoot2.0進行構建
  • 能夠匹配任何任何請求屬性
  • 可以對路由指定Predicates和Filters
  • 集成斷路器
  • 集成Spring Cloud服務發現
  • 易於編寫的Predicates和Filters
  • 支持請求限流
  • 支持路徑重寫

三大概念

路由:路由是構建網關的基本模塊,它由ID,目標URI,一系列的斷言Predicates和過濾器Filters組成,如果斷言爲true,則匹配該路由。

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

過濾:Spring框架中GatewayFilter的實例,使用過濾器,可以載請求被路由前或者後對請求進行修改。

一個web請求,通過一些匹配條件,定位到真正的服務節點,在這個轉發的過程中,進行一些精細化的控制。

  • predicate就是匹配條件
  • filter就是攔截器
  • predicate + filter + 目標uri實現路由route

工作流程

下圖總體上描述了Spring Cloud Gateway的工作流程:

  1. 客戶端向Spring Cloud Gateway發出請求。

  2. 如果Gateway Handler Mapping確定請求與路由匹配,則將其發送到Gateway Web Handler。

  3. Handler通過指定的過濾器鏈將請求發送到我們實際的服務執行業務邏輯,然後返回。

  4. 過濾器由虛線分隔的原因是,過濾器可以在發送代理請求之前或之後執行邏輯。

核心:路由轉發+過濾器鏈

Gateway服務搭建

創建模塊,引入依賴

引入spring-cloud-starter-gateway核心組件,其次,這個服務本身也許要註冊進Eureka,因此引入spring-cloud-starter-netflix-eureka-client

        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

注意:不能引入spring-boot-starter-web依賴

編寫yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          uri: http://localhost:8001          #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/**         # 斷言,路徑相匹配的進行路由

        - id: payment_routh2 #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          uri: http://localhost:8001          #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/lb/**         # 斷言,路徑相匹配的進行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 請求頭要有X-Request-Id屬性並且值爲整數的正則表達式

eureka:
  instance:
    hostname: cloud-gateway-service
    instance-id: gateway9527
    prefer-ip-address: true
  client: #服務提供者provider註冊進eureka服務列表內
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

這段yml配置內容比較多:

  • 確定該服務的端口號9527。
  • 確定了application.name爲cloud-gateway。
  • 開啓從註冊中心動態創建路由的功能,利用微服務名進行路由。
  • 定義了兩段路由,每段路由標明:id,uri和Predicate。
  • 將該服務註冊進eureka的服務列表。

主啓動類

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class, args);
    }
}

測試啓動

依次啓動7001註冊中心,8001服務和9527網關服務,訪問localhost:7001/

由於網關的存在,我們可以訪問:http://localhost:9527/payment/1,能夠成功訪問到8001服務的接口。

Gateway的網關配置方式

yml配置

我們之前已經配置過了:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          uri: http://localhost:8001          #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/**         # 斷言,路徑相匹配的進行路由

注入RouteLocator的Bean

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {

        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

        routes.route("path_route",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();

        return routes.build();
    }
}

配置動態路由

原理:默認情況下Gateway會根據註冊中心註冊的服務列表,以註冊中心上微服務名爲路徑創建動態動態路由進行轉發,從而實現動態路由的功能。

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          uri: lb://cloud-payment-service  #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/**         # 斷言,路徑相匹配的進行路由

        - id: payment_routh2 #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          uri: lb://cloud-payment-service #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/lb/**         # 斷言,路徑相匹配的進行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 請求頭要有X-Request-Id屬性並且值爲整數的正則表達式

訪問localhost:9527/payment/lb,根據獲取到的服務的路由地址,動態切換路由。

Route Predicate Factories

https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories

Spring Cloud Gateway matches routes as part of the Spring WebFlux HandlerMapping infrastructure. Spring Cloud Gateway includes many built-in route predicate factories. All of these predicates match on different attributes of the HTTP request. You can combine multiple route predicate factories with logical and statements.

SpringCloud Gateway將路由匹配作爲Spring WebFlux HandlerMapping基礎架構的一部分。

Spring Cloud Gateway包括許多內置的Route Predicate工廠,所有這些Predicate都與HTTP請求的不同屬性匹配,多個RoutePredicate工廠可以進行組合。

SpringCloud Gateway創建Route對象時,使用RoutePredicateFactory創建Predicate對象,Predicate對象可以賦值給Route。

你可以通過and邏輯組合多種route predicate factories。舉幾個例子:

舉個例子

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          #uri: http://localhost:8001          #匹配後提供服務的路由地址
          uri: lb://cloud-payment-service #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/**         # 斷言,路徑相匹配的進行路由
            - Before=2020-11-26T21:27:26.256+08:00[Asia/Shanghai]

        - id: payment_routh2 #payment_route    #路由的ID,沒有固定規則但要求唯一,建議配合服務名
          #uri: http://localhost:8001          #匹配後提供服務的路由地址
          uri: lb://cloud-payment-service #匹配後提供服務的路由地址
          predicates:
            - Path=/payment/lb/**         # 斷言,路徑相匹配的進行路由
            - After=2020-11-25T22:42:23.608+08:00[Asia/Shanghai]
            - Cookie=username,hyh

cookie可以通過curl命令測試:

$ curl http://localhost:9527/payment/lb --cookie "username=hyh"

header測試:

$ http://localhost:9527/payment/lb --H "X-Request-Id:123"

完整版列舉

        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]  # 在..之前可以匹配
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]   # 在..之後可以匹配
        - Between=xxx,xxx										# 在..之間可以匹配
        - Cookie=chocolate, ch.p								# 帶cookie
        - Header=X-Request-Id, \d+								# 規定header
        - Host=**.somehost.org,**.anotherhost.org				# 規定host
        - Method=GET,POST										# 規定請求方法
        - Path=/red/{segment},/blue/{segment}					# 規定請求路徑
        - Query=green											# 規定請求參數
        - RemoteAddr=192.168.1.1/24								# 規定remoteAddr
        
        # 將80%的流量轉到weighthigh.org,20%轉到weightlow.org
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

Filter

Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.

路由過濾器允許以某種方式修改傳入的HTTP請求或傳出的HTTP響應。

路由過濾器適用於特定路由。

Spring Cloud Gateway包括許多內置的GatewayFilter工廠,感興趣可以查看官網。

自定義過濾器

@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("================come in myGlobalFilter" + new Date() + "==================");
        String name = exchange.getRequest().getQueryParams().getFirst("name");
        if (name == null || name.length() == 0) {
            log.info("=======================非法用戶,name爲null==============================");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        log.info("=======welcome! name =  " + name + "=======================");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

源碼下載

本系列文章爲《尚硅谷SpringCloud教程》的學習筆記【版本稍微有些不同,後續遇到bug再做相關說明】,主要做一個長期的記錄,爲以後學習的同學提供示例,代碼同步更新到Gitee:https://gitee.com/tqbx/spring-cloud-learning,並且以標籤的形式詳細區分每個步驟,這個系列文章也會同步更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章