1. 程式人生 > 其它 >Alibaba微服務元件 - Gateway(二) Spring Cloud Gateway快速開始

Alibaba微服務元件 - Gateway(二) Spring Cloud Gateway快速開始

2.1 環境搭建

2.1.1 引入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2.1.2 編寫yml配置檔案

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: http://localhost:8020  # 需要轉發的地址
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字串所以可以這樣寫,自動對映
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add

2.1.3 整合Nacos

引入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

編寫yml配置檔案

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略 order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字串所以可以這樣寫,自動對映
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
    # 註冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

簡寫: 去掉關於路由的配置,自動尋找服務

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      discovery:
        locator:
          enabled: true # 是否啟動我們的自動識別nacos服務  可以直接用服務名作為字首訪問,斷言也是根據服務名做斷言路由到我們的真實服務地址
                        # http://localhost:8088/order-server/order/add

    # 註冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

測試(這時候,就發現只要按照閘道器地址/微服務/介面的格式去訪問,就可以得到成功響應)

2.2 路由斷言工廠(Route Predicate Factories)配置(區域性,只針對某一個路由)

作用: 當請求gateway的時候,  使用斷言對請求進行匹配, 如果匹配成功就路由轉發, 如果匹配失敗就返回404
型別:內建,自定義

2.2.1 內建斷言工廠

官方文件:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

  • 基於Datetime型別的斷言工廠
    此型別的斷言根據時間做判斷,主要有三個:
    AfterRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否晚於指定日期
    BeforeRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否早於指定日期
    BetweenRoutePredicateFactory: 接收兩個日期引數,判斷請求日期是否在指定時間段內
    ZonedDateTime.now()
spring:
 cloud:
   gateway:
     routes:
     - id: after_route
       uri: https://example.org
       predicates:
       - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  • 基於遠端地址的斷言工廠
    RemoteAddrRoutePredicateFactory:接收一個IP地址段,判斷請求主機地址是否在地址段中
    ‐ RemoteAddr=192.168.1.1/24
  • 基於Cookie的斷言工廠
    CookieRoutePredicateFactory:接收兩個引數,cookie 名字和一個正則表示式。 判斷請求
    cookie是否具有給定名稱且值與正則表示式匹配。
    ‐ Cookie=chocolate, ch.
  • 基於Header的斷言工廠
    HeaderRoutePredicateFactory:接收兩個引數,標題名稱和正則表示式。  判斷請求Header是否具有給定名稱且值與正則表示式匹配。
    ‐ Header=X‐Request‐Id, \d+
  • 基於Host的斷言工廠
    HostRoutePredicateFactory:接收一個引數,主機名模式。判斷請求的Host是否滿足匹配規則。
    ‐ Host=**.testhost.org
  • 基於Method請求方法的斷言工廠
    MethodRoutePredicateFactory:接收一個引數,判斷請求型別是否跟指定的型別匹配。
    ‐ Method=GET
  • 基於Path請求路徑的斷言工廠
    PathRoutePredicateFactory:接收一個引數,判斷請求的URI部分是否滿足路徑規則。
    ‐ Path=/foo/{segment}
  • 基於Query請求引數的斷言工廠
    QueryRoutePredicateFactory :接收兩個引數,請求param和正則表示式, 判斷請求引數是否具有給定名稱且值與正則表示式匹配。
    - Query=baz, ba.
  • 基於路由權重的斷言工廠
    WeightRoutePredicateFactory:接收一個[組名,權重], 然後對於同一個組內的路由按照權重轉發
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

2.2.2 自定義斷言工廠

1. 必須spring元件 注入bean(@Component)
2. 類必須加上RoutePredicateFactory作為結尾
3. 必須繼承AbstractRoutePredicateFactory
4. 必須宣告靜態內部類   宣告屬性來接收 配置檔案中對應的斷言的資訊
5. 需要結合shortcutFieldOrder進行繫結
6. 通過apply進行邏輯判斷  true就是匹配成功   false匹配失敗

注意: 命名需要以 RoutePredicateFactory 結尾,必須注入到spring中

package com.xiexie.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @Description 自定義斷言工廠
 * @Date 2022-04-19 13:49
 * @Author xie
 */
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

    public CheckAuthRoutePredicateFactory() {
        super(CheckAuthRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 必須跟config中的屬性名進行繫結
        return Arrays.asList("name");
    }

    @Override
    public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                if (StringUtils.isEmpty(config.getName()) || !"xiexie".equals(config.getName())) {
                    return false;
                }
                return true;
            }

            @Override
            public String toString() {
                return String.format("CheckAuth: name=%s", config.getName());
            }
        };
    }

    /**
     * 用於接收配置檔案中 斷言的資訊
     */
    @Validated
    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略 order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字串所以可以這樣寫,自動對映
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
            # 自定義CheckAuth斷言工廠
            - CheckAuth=xiexie
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
    # 註冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

2.3 過濾器工廠( GatewayFilter Factories)配置(區域性,只針對某一個路由)

Gateway 內建了很多的過濾器工廠,我們通過一些過濾器工廠可以進行一些業務邏輯處理器,比如新增剔除響應頭,新增去除引數等

官方文件:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

2.3.1 內建過濾器工廠

過濾器工廠 作用 引數
AddRequestHeader 為原始請求新增Header Header的名稱及值
AddRequestParameter 為原始請求新增請求引數 引數名稱及值
AddResponseHeader 為原始響應新增Header Header的名稱及值
DedupeResponseHeader 剔除響應頭中重複的值 需要去重的Header名稱及去重策略
Hystrix 為路由引入Hystrix的斷路器保護 HystrixCommand的名稱
FallbackHeaders 為fallbackUri的請求頭中新增具體的異常資訊 Header的名稱
PrefixPath 為原始請求路徑新增字首 字首路徑
PreserveHostHeader 為請求新增一個preserveHostHeader=true 的屬性,路由過濾器會檢查該屬性以決定是否要傳送原始的Host
RequestRateLimiter 用於對請求限流,限流演算法為令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 將原始請求重定向到指定的URL http狀態碼及重定向的url
RemoveHopByHopHeadersFilter 為原始請求刪除IETF組織規定的一系列Header 預設就會啟用,可以通過配置指定僅刪除哪些Header
RemoveRequestHeader 為原始請求刪除某個Header Header名稱
RemoveResponseHeader 為原始響應刪除某個Header Header名稱
RewritePath 重寫原始的請求路徑 原始路徑正則表示式以及重寫後路徑的正則表示式
RewriteResponseHeader 重寫原始響應中的某個Header Header名稱、值的正則表示式,重寫後的值
SaveSession 在轉發請求之前,強制執行WebSession::save操作
secureHeaders 為原始響應新增一系列起安全作用的響應頭 無,支援修改這些安全響應頭的值
SetPath 修改原始的請求路徑 修改後的路徑
SetResponseHeader 修改原始響應中某個Header的值 Header名稱,修改後的值
SetStatus 修改原始響應的狀態碼 HTTP 狀態碼,可以是數字,也可以是字串
StripPrefix 用於截斷原始請求的路徑 使用數字表示要截斷的路徑的數量
Retry 針對不同的響應進行重試 retries、statuses、methods、series
RequestSize 設定允許接收最大請求包的大 小。如果請求包大小超過設定的值,則返回 413 Payload Too Large 請求包大小,單位為位元組,預設值為5M
ModifyRequestBody 在轉發請求之前修改原始請求體內容 修改後的請求體內容
ModifyResponseBody 修改原始響應體的內容 修改後的響應體內容

2.3.1.1 新增請求頭(上下文參考-全量配置檔案)

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略(loadbalancer) order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字串所以可以這樣寫,自動對映
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add   http://order-server/order-serv/order/add
            - Path=/order-serv/**
            # 自定義CheckAuth斷言工廠
            - CheckAuth=xiexie # 資料放在自定義斷言工廠的config中,根據config對映到的資料值進行判斷是否匹配到
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
            # 其他的內建過濾器
            - AddRequestHeader=X-Request-color, red # 新增請求頭引數
            # - AddRequestParameter=color, blue # 新增請求引數
            # - PrefixPath=/mall‐order # 新增字首 對應微服務需要配置context‐path: /mall‐order
            # - RedirectTo=302, https://www.baidu.com/ # 重定向到百度
            # - CheckAuth=name, xiexie # 資料放在自定義的過濾器中,根據config對映到的資料進行判斷是否放行
    # 註冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
    System.out.println("gateWay獲取請求頭X‐Request‐color:" + color);
    return color;
}

2.3.1.2 新增請求引數(上下文參考上文全量配置檔案)

- AddRequestParameter=color, blue # 新增請求引數
@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color) {
    System.out.println("gateWay獲取引數color:" + color);
    return color;
}

2.3.1.3 為匹配的路由統一新增字首(上下文參考上文全量配置檔案)

- PrefixPath=/mall‐order # 新增字首 對應微服務需要配置context‐path: /mall‐order
server:
  port: 8020
  servlet:
    context-path: /mall‐order

2.3.1.4 重定向操作(上下文參考上文全量配置檔案)

- RedirectTo=302, https://www.baidu.com/ # 重定向到百度

.....

.....

還有很多,自行參照上面列表測試

2.3.2 自定義過濾器工廠

繼承AbstractNameValueGatewayFilterFactory且我們的自定義名稱必須要以GatewayFilterFactory結尾並交給spring管理

跟自定義的斷言工廠相似

實現過濾工廠

package com.xiexie.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @Description
 * @Date 2022-04-19 15:37
 * @Author xie
 */
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {

    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                Set<String> stringSet = queryParams.keySet();
                if (stringSet.contains(config.getParam()) && config.getValue().equals(queryParams.get(config.getParam()).get(0))) {
                    // 正常請求
                    return chain.filter(exchange);
                } else {
                    // 返回404並結束
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {

        private String param;

        private String value;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

配置檔案中配置(上下文參考上文全量配置檔案)

- CheckAuth=name, xiexie # 資料放在自定義的過濾器中,根據config對映到的資料進行判斷是否放行

2.4 全域性過濾器(Global Filters)配置

區域性過濾器和全域性過濾器區別:

  • 區域性:區域性針對某個路由, 需要在路由中進行配置
  • 全域性:針對所有路由請求, 一旦定義就會投入使用
    GlobalFilter 介面和 GatewayFilter 有一樣的介面定義,只不過, GlobalFilter 會作用於所有路由

2.4.1 自定義全域性過濾器

package com.xiexie.gateway.filter.global;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Description 自定義全域性過濾器
 * @Date 2022-04-19 16:30
 * @Author xie
 */
@Component
public class GlobalLogFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(GlobalLogFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

2.4.2 Reactor Netty 訪問日誌(gateway自動定義的日誌收集)

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要啟用 Reactor Netty 訪問日誌,請設定 -Dreactor.netty.http.server.accessLogEnabled=true

它必須是 Java 系統屬性(設定JVM環境變數的),而不是 Spring Boot 屬性。
java -jar xxxGatewat.jar -Dreactor.netty.http.server.accessLogEnabled=true

可以將日誌記錄系統配置為具有單獨的訪問日誌檔案。(比如配置在Logback)

2.5  Gateway跨域配置(CORS Configuration)

通過yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

2.5.1 通過yml配置的方式

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 閘道器gateway配置
    gateway:
      # 跨域配置
      globalcors:
        cors-configurations:
          '[/**]':  # 允許跨域訪問的資源
            allowedOrigins: "*"  # 跨域允許的來源
            allowedMethods:  # 跨域允許的method
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

2.5.1 通過java程式碼方式

package com.xiexie.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @Description 跨域配置
 * @Date 2022-04-19 17:06
 * @Author xie
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*"); // 允許跨域的method
        config.addAllowedOrigin("*"); // 允許跨域的來源
        config.addAllowedHeader("*"); // 允許跨域的header頭

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config); // 允許跨域訪問的資源
        return new CorsWebFilter(source);
    }
}

2.6  gateway整合sentinel流控降級