SpringCloud入門 服務網關 Gateway


服務網關

zuul路由網關,內部出問題,建議不再使用。zuul原始開發人員被挖走了,zuul2中開發人員出現分歧。。。

學習方式:image-20200827151840744

Gateway新一代網關,主要這個!

Gateway是什么?

SpringCloud Gateway使用的Webflux中的reactor-netty響應式編程組件,底層使用了Netty通訊框架。

官方文檔https://docs.spring.io/spring-cloud-gateway/docs/2.2.4.RELEASE/reference/html/

image-20200827152713386

Cloud全家桶中有個很重要的組件就是網關,在1.x版本中都是采用的Zuul網關;
但在2.x版本中,zuul的升級一直跳票,SpringCloud最后自己研發了一個網關替代Zuul,
那就是SpringCloud Gateway一句話: gateway是原zuul1.x版的替代。

Gateway是在Spring生態系統之上構建的API網關服務,基於Spring 5,Spring Boot2和Project Reactor等技術。
Gateway旨在提供一種簡單而有效的方式來對API進行路由,以及提供一些強大的過濾器功能,例如:熔斷、限流、重試等。

SpringCloud Gateway是Spring Cloud的一個全新項目,基於Spring 5.0+Spring Boot2.0和Project Reactor等技術開發的網關,它旨在為微服務架構提供—種簡單有效的統一的API路由管理方式。

SpringCloud Gateway作為Spring Cloud生態系統中的網關,目標是替代Zul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高性能版本進行集成,仍然還是使用的Zuul 1.x非Reactor模式的老版本。而為了提升網關的性能,SpringCloud Gateway是基於WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通信框架Netty.

Spring Cloud Gateway的目標提供統一的路由方式且基於Filter鏈的方式提供了網關基本的功能,例如:安全,監控/指標,和限流。

搜索 B站 韓順平 netty·~~~~~

能干嘛?

image-20200827153136726

微服務結構中網關的位置

image-20200827153200150

2、Gateway非阻塞異步模型

我們為什么選擇Gateway?

1、neflix不太靠譜,zuul2.0一直跳票,遲遲不發布

一方面因為Zuul1.0已經進入了維護階段,而且Gateway是SpringCloud團隊研發的,是親兒子產品,值得信賴。而且很多功能Zuul都沒有用起來也非常的簡單便捷。

Gateway是基於異步非阻塞模型上進行開發的,性能方面不需要擔心。雖然Netflix早就發布了最新的Zuul 2.x,但Spring Cloud貌似沒有整合計划。而且Netflix相關組件都宣布進入維護期;不知前景如何?

多方面綜合考慮Gateway是很理想的網關選擇。

2、SpringCloud Gateway具有如下特性

基於Spring Framework 5,Project Reactor和Spring Boot2.0進行構建;
動態路由:能夠匹配任何請求屬性;
可以對路由指定Predicate(斷言)和Filter(過濾器)﹔
集成Hystrix的斷路器功能;
集成Spring Cloud 服務發現功能;
易於編寫的Predicate(斷言)和Filter(過濾器);
請求限流功能;
支持路徑重寫。

3、SpringCloud Gateway與Zuul的區別

在SpringCloud Finchley 正式版之前,Spring Cloud推薦的網關是Netflix提供的Zuul:

1、Zuul 1.x,是一個基於阻塞1O的API Gateway

2、Zuul 1.x基於Servlet2.5使用阻塞架構它不支持任何長連接(如WebSocket)Zuul的設計模式和Nginx較像,每次VО操作都是從工作線程中選擇一個執行,請求線程被阻塞到工作線程完成,但是差別是Nginx用C++實現,Zuul用Java實現,而VM本身會有第
—次加載較慢的情況,使得Zuul的性能相對較差。

3、Zuul 2.x理念更先進,想基於Netty非阻塞和支持長連接,但SpringCloud目前還沒有整合。Zul 2x的性能較Zuul 1.x有較大提升
。在性能方面,根據官方提供的基准測試,Spring Cloud Gateway的RPS(每秒請求數)是Zuul的1.6倍。

4、Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot2之上,使用非阻塞API。

5、Spring Cloud Gateway還支持WebSocket,並且與Spring緊密集成擁有更好的開發體驗

Zuul的模型

Springcloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是傳統的Servlet IO處理模型。
學過尚硅谷web中期課程都知道一個題目,Servlet的生命周期?servlet由servlet container進行生命周期管理.
container啟動時構造servlet對象並調用servlet init(進行初始化;
container運行時接受請求,並為每個請求分配一個線程(一般從線程池中獲取空閑線程)然后調用service).
container關閉時調用servlet destory0銷毀servlet;

image-20200827154424697

上述模式的缺點:
servlet是一個簡單的網絡l0模型,當請求進入servlet container時,servlet container就會為其綁定一個線程,在並發不高的場景下這種模型是適用
的。但是一旦高並發(比如抽風用jemetel壓),線程數量就會上漲,而線程資源代價是昂貴的(上線文切換,內存消耗大)嚴重影響請求的處理時間。
在一些簡單業務場景下,不希望為每個request分配一個線程,只需要1個或幾個線程就能應對極大並發的請求,這種業務場景下serviet模型沒有優勢
所以Zuul1.X是基於servlet之上的一個阻塞式處理模型,即spring實現了處理所有request請求的一個servlet (DispatcherServlet)並由該servlet阻塞式處理處理。所以Springcloud Zuul無法擺脫servlet模型的弊端

Gateway模型

WebFlux是什么?

查看官網:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux

Spring框架中包含的原始Web框架Spring Web MVC是專門為Servlet API和Servlet容器而構建的。反應性堆棧Web框架Spring WebFlux在更高版本5.0中添加。它是完全非阻塞的,支持 Reactive Streams背壓,並在Netty,Undertow和Servlet 3.1+容器等服務器上運行。
這兩個Web框架均反映了其源模塊的名稱(spring-webmvc和 spring-webflux),並在Spring Framework中並存。每個模塊都是可選的。應用程序可以使用一個模塊或另一個模塊,或者在某些情況下可以同時使用這兩個模塊,例如,帶有react的Spring MVC控制器WebClient。

說明:

傳統的Web框架,比如說: struts2,springmvc等都是基於Servlet API與Servlet容器基礎之上運行的。
但是
在Servlet3.1之后有了異步非阻塞的支持。而WebFlux是一個典型非阻塞異步的框架,它的核心是基於Reactor的相關APl實現的。相對於傳統的web框架來說,它可以運行在諸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函數式編程(Spring5必須讓你使用java8)

Spring WebFlux是Spring 5.0引入的新的響應式框架,區別於Spring MVC,它不需要依賴Servlet API,它是完全異步非阻塞的,並且基於Reactor來實現響應式流規范。

3、Gateway工作流程

三大核心概念:

1、Route(路由)

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

2、Predicate(斷言)

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

3、Filter(過濾)

指的是Spring框架中GatewayFilter的實例,使用過濾器,可以再請求被路由前或者之后對請求進行修改。

總體:

image-20200827155707456

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

predicate就是我們的匹配條件;而filter,就可以理解為一個無所不能的攔截器。有了這倆個元素,再加上目標uri,就可以實現一個具體的路由了。

工作流程

核心邏輯:路由轉發和執行過濾鏈。

image-20200827160252950

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

客戶端向Spring Cloud Gateway 發出請求。然后在Gateway Handler Mapping 中找到與請求相匹配的路由,將其發送到Gateway Web Handler。

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

過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(“pre”)或之后(“post”)執行業務邏輯。

Filter在“pre”類型的過濾器可以做參數校驗、權限校驗、流量監控、日志輸出、協議轉換等,在“post”類型的過濾器中可以做響應內容、響應頭的修改,日志的輸出,流量監控等有着非常重要的作用。

4、Gateway搭建

新建模塊cloud-gateway-gateway9527

引入pom

<properties>
    <spring.boot.version>2.2.9.RELEASE</spring.boot.version>
</properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fage</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

注意 springboot 版本問題,要不然一直報錯。。。。。先是netty后是webFlux。。

yml配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: payment_routh1  # 路由id,沒有固定規則但要求唯一,建議配合服務名使用
          uri: http://localhost:8001 # 匹配后提供服務的路由地址
          predicates:
            - Path=/payment/get/** # 斷言,路徑相匹配的進行路由
        - id: payment_routh2  # 路由id,沒有固定規則但要求唯一,建議配合服務名使用
          uri: http://localhost:8001 # 匹配后提供服務的路由地址
          predicates:
            - Path=/payment/loadBalanced/** # 斷言,路徑相匹配的進行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client:    #  服務提供者provider注冊金eureka服務列表內
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka/

驗證:

image-20200831201519624

5、gateway配置路由的方式

1、yml配置

如上所示

2、硬編碼方式

package com.fage.springcloud.config;

import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author lin
 * @version 1.0
 * @date 2020/8/31 20:36
 * @Description TODO
 */
@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder) {
        return routeBuilder.routes().route("1234", predicateSpec -> {
            return predicateSpec.path("/guonei")
                    .uri("http://news.baidu.com/guonei");
        }).build();
    }

}

配置動態路由

將制定ip修改為命名形式,使用網關實現負載均衡等。

image-20200903094914216

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

修改yml配置文件即可

  ## 開啟從注冊中心動態創建路由的功能,利用微服務進行路由
1、  discovery:
    locator:
      enabled: true
      
 2、  uri: lb://cloud-payment-service  #  使用命名的方式
spring:
  application:
    name: cloud-gateway-service
  cloud:
    gateway:
      ## 開啟從注冊中心動態創建路由的功能,利用微服務進行路由
      discovery:
        locator:
          enabled: true
      routes:
        - id: payment_routh1  # 路由id,沒有固定規則但要求唯一,建議配合服務名使用
          #uri: http://localhost:8001 # 匹配后提供服務的路由地址
          uri: lb://cloud-payment-service  #  使用命名的方式
          predicates:
            - Path=/payment/get/** # 斷言,路徑相匹配的進行路由
        - id: payment_routh2  # 路由id,沒有固定規則但要求唯一,建議配合服務名使用
          uri: http://localhost:8001 # 匹配后提供服務的路由地址
          predicates:
            - Path=/payment/loadBalanced/** # 斷言,路徑相匹配的進行路由

測試:

image-20200903100631385

image-20200903100951666

6、常用的Predicate斷言

Predicate就是為了實現一組匹配規則,讓請求過來找到對應的Route進行處理,不滿足的則不允許通過。

image-20200903101216459

Spring Cloud Gateway將路由匹配作為Spring WebFlux HandlerMapping基礎架構的一部分。
Spring Cloud Gateway包括許多內置的Route Predicate工廠。所有這些Predicate都與HTTP請求的不同屬性匹配。多個Route Predicate工廠可以進行組合。

Spring Cloud Gateway創建Route對象時,使用RoutePredicateFactory 創Predicate對象可以賦值給Route。Spring Cloud Gateway包含許多內置的Route Predicate Factories.

所有這些謂詞都匹配HTTP請求的不同屬性。多種謂詞丁廠可以組合邏輯and。

如何獲取到after的時間

ZonedDateTime.now() = 2020-09-03T10:41:08.988+08:00[Asia/Shanghai]

public class TestMain {

    public static void main(String[] args) {
        System.out.println("ZonedDateTime.now() = " + ZonedDateTime.now());
    }

}

時間要求:after、before、between

之后
-After=ZonedDateTime.now() = 2020-09-03T10:41:08.988+08:00[Asia/Shanghai]

之前
-Before=ZonedDateTime.now() = 2020-09-03T10:41:08.988+08:00[Asia/Shanghai]

倆者之間
-Between=ZonedDateTime.now() = 2020-09-03T10:41:08.988+08:00[Asia/Shanghai],ZonedDateTime.now() = 2021-09-03T10:41:08.988+08:00[Asia/Shanghai]


請求參數:cookie、header、host、method、query

cookie:需要倆個參數,一個是Cookie name,一個是正則表達式
路由規則則會通過獲取對應的Cookie name值和正則表達式去匹配,如果匹配上就會執行路由,反之則不執行
- Cookie=username,fagejiang

Header 請求頭參數
#請求頭要有X-request-id屬性並且值為正整數。
- Header=X-request-id,\d+ 

Host 主機
- Host=**。fagejiang.com

Method 方法
- Method=GET

Query
#請求參數名有username並且值要是正數。
- Query=username,\d+ 

Path
#路徑是否包含
- Path=/payment/get/**
終歸是一個扛下了 所有:
 - Path=/payment/get/** # 斷言,路徑相匹配的進行路由
 
 - After=2020-09-03T10:41:08.988+08:00[Asia/Shanghai]
 
 - Before=2021-09-03T10:41:08.988+08:00[Asia/Shanghai]
 
 - Between=2020-09-03T10:41:08.988+08:00[Asia/Shanghai],2021-09-03T10:41:08.988+08:00[Asia/Shanghai]

- Cookie=username,fagejiang

- Header=X-request-id,\d+

- Host=**.fagejiang.com

- Method=GET

- Query=username,\d+

Windows下安裝使用Curl及解決中文亂碼問題

https://blog.csdn.net/leedee/article/details/82685636

注意使用 PowerShell(出錯)和CMD(正常)窗口問題。

7、GateFilter過濾器

Spring Cloud Gateway內置了多種路由過濾器,他們都由GatewayFilter的工廠類產生。

路由過濾器可用於修改進入的Http請求和返回的Http響應,路由過濾器只能指定路由進行使用。

image-20200903113226273

全局過濾器

        - id: payment_routh2  # 路由id,沒有固定規則但要求唯一,建議配合服務名使用
          uri: http://localhost:8001 # 匹配后提供服務的路由地址
          ### 增加過過濾條件
          predicates:
            - Path=/payment/loadBalanced # 斷言,路徑相匹配的進行路由
          filters:
            # 過濾器工廠會在匹配的請求頭上加上一對請求頭,名稱為X-Request-id,值為整數數字
            - AddRequestHeader=X-Request-id, \d+

查看官網:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories

自定義過濾器

image-20200903113537739

@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("*********** come in MyLogGatewayFilter : " + DateUtil.now());
        boolean loadBalanced = exchange.getRequest().getPath().toString().contains("loadBalanced");
        String name = exchange.getRequest().getQueryParams().getFirst("name");
        if (loadBalanced && name == null) {
            log.info("*********** 用戶名為null,非法用戶,o(╥﹏╥)o");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return OrderedConstant.REQUEST_FILTER;
    }
}

調用http://localhost:9527/payment/loadBalanced?name=z3成功

調用http://localhost:9527/payment/loadBalanced失敗

image-20200903144045385

2020-09-03 14:39:45.775 [reactor-http-nio-2] INFO  com.fage.springcloud.filters.MyLogGatewayFilter - *********** come in MyLogGatewayFilter : 2020-09-03 14:39:45
2020-09-03 14:40:39.121 [reactor-http-nio-2] INFO  com.fage.springcloud.filters.MyLogGatewayFilter - *********** come in MyLogGatewayFilter : 2020-09-03 14:40:39
2020-09-03 14:40:39.121 [reactor-http-nio-2] INFO  com.fage.springcloud.filters.MyLogGatewayFilter - *********** 用戶名為null,非法用戶,o(╥﹏╥)o

公眾號發哥講

這是一個稍偏基礎和偏技術的公眾號,甚至其中包括一些可能閱讀量很低的包含代碼的技術文,不知道你是不是喜歡,期待你的關注。

代碼分享

https://gitee.com/naimaohome

微信公眾號 點擊關於我,加入QQ群,即可獲取到代碼以及高級進階視頻和電子書!!

img

如果你覺得文章還不錯,就請點擊右上角選擇發送給朋友或者轉發到朋友圈~

● 掃碼關注我們

據說看到好文章不推薦的人,服務器容易宕機!

本文版權歸 發哥講博客園 共有,原創文章,未經允許不得轉載,否則保留追究法律責任的權利。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM