一文搞定zuul使用

前言

在深入探討Spring Cloud Gateway的細節之前,讓我們了解有關反向代理和api網關模式的一些基礎知識。

什麼是反向代理?

反向代理是代表其他事物進行請求的事物。它的行為更像是簡單的路由。它可以增加基本的安全性和監視功能,但實際上不能做某些高級事情,例如中介或業務流程。NGINX是眾所周知的反向代理伺服器之一。

什麼是API網關?

簡而言之,API Gateway是增強的反向代理,具有更高級的功能,包括編排以及增加的安全性和監視功能。Netflix Zuul(上文介紹),Amazon API Gateway,Apigee以及Spring Cloud Gateway當然是少數著名的api網關實現。

Spring Cloud Gateway主要功能:

  • 建立在Spring Framework 5,Project Reactor(後面我會單獨開篇幅去介紹reactor3)和Spring Boot 2.0之上

  • 能夠匹配任何請求屬性上的路由。

  • 謂詞和過濾器特定於路由。

  • 斷路器集成。

  • Spring Cloud DiscoveryClient集成

  • 易於編寫的謂詞和過濾器

  • 請求速率限制

  • 路徑改寫

Spring Cloud Gateway 對比 Zuul

  • 我知道所有使用Spring Boot的人都想知道,我們已經在使用Zuul。它幾乎提供了與Spring Cloud Gateway相同的功能。那麼,為什麼我們要切換到新框架?對我來說,主要原因是Zuul 1.x沒有反應式編程。它使用阻塞api。現在,如果您正在遷移到Reactive模式以從微服務中獲得更好的性能,並且您將Spring Boot 2與Reactor一起使用。

  • 您可以使用Zuul 2.0,它確實具有Netty的Reactive nonblocking支援。 但是Spring生態系統沒有像Zuul 1.x那樣給予Zuul 2.0的內置支援,這意味著,您必須使用Zuul運行自己的獨立服務。你不能讓它與其他現有的spring微服務集成。Spring團隊並不打算用Spring來增加對它的支援。

  • 所以Spring Cloud Gateway可以與任何微服務集成,使之成為真正的api網關,在一個地方添加身份驗證和其他安全功能。因為推薦使用Spring Cloud Gateway。

Spring Cloud Gateway簡介

Spring Cloud Gateway是Spring Cloud團隊在Spring反應生態系統(Webflux)之上的API網關實現。它提供了一種簡單有效的方法,即使用網關處理程式映射將傳入的請求路由到適當的目的地。Spring Cloud Gateway使用Netty伺服器提供非阻塞非同步請求處理。以下是在Spring Cloud Gateway中請求路由的工作原理的高層流程:

Spring Cloud Gateway架構
Spring Cloud Gateway架構

Spring Cloud Gateway包含3個主要構建模組:

  • Route(路由)

    將此視為我們希望將特定請求路由到的目的地。它由目標URI、必須滿足的條件或技術術語、謂詞和一個或多個篩選器組成。

  • Predicate(謂詞)

    這實際上是一個需要匹配的條件。 例如if條件。如果請求有某些東西-例如,path=blah或請求頭包含foo-bar等。在java技術術語中,它類似於java8函數謂詞Predicate

    具體謂詞類型參考文檔: //docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

  • Filter(過濾器)

    這些是springframeworkwebfilter的實例。在這裡您可以應用修改請求或響應的魔力。框架提供了很多現成的WebFilter。當然,我們討論的是Spring框架。所以,大家休息吧!!!您始終可以使用自己的邏輯添加自己的篩選器。

Spring Cloud Gateway 原理

Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的區別就是 Gateway 的 Filter 只有 pre 和 post 兩種。下面我們簡單了解一下 Gateway 的工作原理圖,如下圖所示。

架構圖
架構圖
  • Predicate 原理

    Predicte由PredicateSpec來構建,主要實現有:

Predicte
Predicte

以PathRoutePredicateFactory為例看下源碼:

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
        synchronized (this.pathPatternParser) {
            pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator());
            config.getPatterns()
                    .forEach(pattern -> {
                        // 構建匹配規則
                        PathPattern pathPattern = this.pathPatternParser.parse(pattern);
                        pathPatterns.add(pathPattern);
                    })
            ;
        }
        return exchange -> {
            PathContainer path = parsePath(exchange.getRequest().getURI().getPath());
            // 匹配過濾
            Optional<PathPattern> optionalPathPattern = pathPatterns.stream().filter(pattern -> pattern.matches(path)).findFirst();
            if (optionalPathPattern.isPresent()) {
                // 如果存在匹配成功的  然後設置匹配成功的PathMatchInfo
                PathPattern pathPattern = optionalPathPattern.get();
                traceMatch("Pattern", pathPattern.getPatternString(), path, true);
                PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
                putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
                return true;
            } else {
                traceMatch("Pattern", config.getPatterns(), path, false);
                return false;
            }
        };
    }
  • Filter 原理

    Filter分兩種,一種GatewayFilter(網關路由過濾器),一種GlobalFilter(全局路由器)

    - GlobalFilter
    為請求業務以及路由的URI轉換為真實業務服務的請求地址的核心過濾器,不需要配置,模式系統初始化時載入,並作用在每個路由上。

    GlobalFilter類型參考文檔

    • GatewayFilter

    Gateway內置了不少可用的GatewayFilter,具體如下:

    GatewayFilter
    GatewayFilter

    下面以OrderedGatewayFilter為例子看下源碼:

    /**
     * 排序的網關路由過濾器,用於包裝真實的網關過濾器,已達到過濾器可排序
     */
    public class OrderedGatewayFilter implements GatewayFilter, Ordered {

        //目標過濾器
        private final GatewayFilter delegate;
        //排序欄位
        private final int order;

        public OrderedGatewayFilter(GatewayFilter delegate, int order) {
            this.delegate = delegate;
            this.order = order;
        }
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 讓下個過濾器過濾
            return this.delegate.filter(exchange, chain);
        }
    }
     具體GatewayFilter類型參考文檔: `//docs.spring.io/spring-cloud-gateway/docs/2.2.8.BUILD-SNAPSHOT/reference/html/#gatewayfilter-factories`

Spring Cloud Gateway 入門

  1. ###引入依賴
  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
  1. 使用RouteLocator去構建url路由
@SpringBootApplication
public class GatewayClientApplication {

    @Value("${test.uri}")
    private String uri;

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                //basic proxy
                .route(r -> r.path("/order/**")
                        .uri(uri)
                ).build();
    }

    public static void main(String[] args) {
        SpringApplication.run(GatewayClientApplication.class, args);
    }
}
  1. application.ymal方式

假設test1-service和test2-service的服務地址分別是//127.0.0.1:8070,//127.0.0.1:8060

例如下面的寫法中,test1-service最終轉發後的路徑就是://127.0.0.1:8070/** 去除了test1-service路徑

test2-service最終路徑 ://127.0.0.1:8060/test2-service/** 不做路徑剝離直接請求。

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true
      routes:
        - id: test-1
          uri: lb://test1-service  #負載均衡url
          order: 8001 #定義順序
          predicates: #謂詞
            - Path=/test1-service/**
          filters: #過濾器  
            - AddResponseHeader=X-Response-test1, test1
        - id: test-2
          uri: lb://test2-service
          order: 8003
          predicates:
            - Path=/test2-service/oauth/token
          filters:   
            - AddResponseHeader=X-Response-test2, test2

END

下篇詳細介紹 Spring Cloud Gateway 高級用法

歡迎關注公眾號!
公眾號回復:入群 ,掃碼加入我們交流群!
掃碼關注公眾號獲取更多學習資料