Gateway網(wǎng)關(guān)

代碼參考:

Gitee:[https://gitee.com/xn2001/cloudcode/tree/master/07-cloud-gateway](https://gitee.com/xn2001/cloudcode/tree/master/07-cloud-gateway)

GitHub:[https://github.com/lexinhu/cloudcode/tree/master/07-cloud-gateway](https://github.com/lexinhu/cloudcode/tree/master/07-cloud-gateway)

Spring Cloud Gateway 是 Spring Cloud 的一個(gè)全新項(xiàng)目,該項(xiàng)目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等響應(yīng)式編程和事件流技術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單有效的統(tǒng)一的 API 路由管理方式。

Gateway 網(wǎng)關(guān)是我們服務(wù)的守門神,所有微服務(wù)的統(tǒng)一入口。

網(wǎng)關(guān)的核心功能特性

  • 請(qǐng)求路由
  • 權(quán)限控制
  • 限流


權(quán)限控制:網(wǎng)關(guān)作為微服務(wù)入口,需要校驗(yàn)用戶是是否有請(qǐng)求資格,如果沒有則進(jìn)行攔截。

路由和負(fù)載均衡:一切請(qǐng)求都必須先經(jīng)過 gateway,但網(wǎng)關(guān)不處理業(yè)務(wù),而是根據(jù)某種規(guī)則,把請(qǐng)求轉(zhuǎn)發(fā)到某個(gè)微服務(wù),這個(gè)過程叫做路由。當(dāng)然路由的目標(biāo)服務(wù)有多個(gè)時(shí),還需要做負(fù)載均衡。

限流:當(dāng)請(qǐng)求流量過高時(shí),在網(wǎng)關(guān)中按照下流的微服務(wù)能夠接受的速度來放行請(qǐng)求,避免服務(wù)壓力過大。

在 SpringCloud 中網(wǎng)關(guān)的實(shí)現(xiàn)包括兩種:

  • gateway
  • zuul

Zuul 是基于 Servlet 實(shí)現(xiàn),屬于阻塞式編程。而 Spring Cloud Gateway 則是基于 Spring5 中提供的WebFlux,屬于響應(yīng)式編程的實(shí)現(xiàn),具備更好的性能。

入門使用

1、創(chuàng)建 SpringBoot 工程 gateway,引入網(wǎng)關(guān)依賴
2、編寫啟動(dòng)類
3、編寫基礎(chǔ)配置和路由規(guī)則
4、啟動(dòng)網(wǎng)關(guān)服務(wù)進(jìn)行測(cè)試

<!--網(wǎng)關(guān)-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服務(wù)發(fā)現(xiàn)依賴-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

創(chuàng)建 application.yml 文件,內(nèi)容如下:

server:
  port: 10010 # 網(wǎng)關(guān)端口
spring:
  application:
    name: gateway # 服務(wù)名稱
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 網(wǎng)關(guān)路由配置
        - id: user-service # 路由id,自定義,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目標(biāo)地址 http就是固定地址
          uri: lb://userservice # 路由的目標(biāo)地址 lb就是負(fù)載均衡,后面跟服務(wù)名稱
          predicates: # 路由斷言,也就是判斷請(qǐng)求是否符合路由規(guī)則的條件
            - Path=/user/** # 這個(gè)是按照路徑匹配,只要以/user/開頭就符合要求

我們將符合Path 規(guī)則的一切請(qǐng)求,都代理到 uri參數(shù)指定的地址。

上面的例子中,我們將 /user/** 開頭的請(qǐng)求,代理到 lb://userservice,其中 lb 是負(fù)載均衡(LoadBalance),根據(jù)服務(wù)名拉取服務(wù)列表,實(shí)現(xiàn)負(fù)載均衡。

重啟網(wǎng)關(guān),訪問 http://localhost:10010/user/1 時(shí),符合 /user/** 規(guī)則,請(qǐng)求轉(zhuǎn)發(fā)到 uri:http://userservice/user/1

多個(gè) predicates 的話,要同時(shí)滿足規(guī)則,下文有例子。

流程圖


路由配置包括:

1、路由id:路由的唯一標(biāo)示
2、路由目標(biāo)(uri):路由的目標(biāo)地址,http代表固定地址,lb代表根據(jù)服務(wù)名負(fù)載均衡
3、路由斷言(predicates):判斷路由的規(guī)則
4、路由過濾器(filters):對(duì)請(qǐng)求或響應(yīng)做處理

斷言工廠

我們?cè)谂渲梦募袑懙臄嘌砸?guī)則只是字符串,這些字符串會(huì)被 Predicate Factory 讀取并處理,轉(zhuǎn)變?yōu)槁酚膳袛嗟臈l件。

例如Path=/user/** 是按照路徑匹配,這個(gè)規(guī)則是由

org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory類來處理的,像這樣的斷言工廠在 Spring Cloud Gateway 還有十幾個(gè)


官方文檔:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
一般的,我們只需要掌握 Path,加上官方文檔的例子,就可以應(yīng)對(duì)各種工作場(chǎng)景了。

predicates:
  - Path=/order/**
  - After=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] #2031年之后才能訪問

過濾器工廠

GatewayFilter 是網(wǎng)關(guān)中提供的一種過濾器,可以對(duì)進(jìn)入網(wǎng)關(guān)的請(qǐng)求和微服務(wù)返回的響應(yīng)做處理。

Spring提供了31種不同的路由過濾器工廠。
官方文檔:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories


下面我們以 AddRequestHeader 為例:

需求:給所有進(jìn)入 userservice 的請(qǐng)求添加一個(gè)請(qǐng)求頭:sign=xn2001.com is eternal

只需要修改 gateway 服務(wù)的 application.yml文件,添加路由過濾即可。

spring:
  cloud:
    gateway:
      routes: # 網(wǎng)關(guān)路由配置
        - id: user-service # 路由id,自定義,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目標(biāo)地址 http就是固定地址
          uri: lb://userservice # 路由的目標(biāo)地址 lb就是負(fù)載均衡,后面跟服務(wù)名稱
          predicates: # 路由斷言,也就是判斷請(qǐng)求是否符合路由規(guī)則的條件
            - Path=/user/** # 這個(gè)是按照路徑匹配,只要以/user/開頭就符合要求
          filters:
            - AddRequestHeader=sign, xn2001.com is eternal # 添加請(qǐng)求頭

如何驗(yàn)證,我們修改 userservice 中的一個(gè)接口

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "sign", required = false) String sign) {
    log.warn(sign);
    return userService.queryById(id);
}

重啟兩個(gè)服務(wù),訪問:http://localhost:10010/user/1

可以看到控制臺(tái)打印出了這個(gè)請(qǐng)求頭


當(dāng)然,Gateway 也是有全局過濾器的,如果要對(duì)所有的路由都生效,則可以將過濾器工廠寫到 default-filters 下:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=sign, xn2001.com is eternal # 添加請(qǐng)求頭

全局過濾器

上面介紹的過濾器工廠,網(wǎng)關(guān)提供了 31 種,但每一種過濾器的作用都是固定的。如果我們希望攔截請(qǐng)求,做自己的業(yè)務(wù)邏輯則沒辦法實(shí)現(xiàn)。

全局過濾器的作用也是處理一切進(jìn)入網(wǎng)關(guān)的請(qǐng)求和微服務(wù)響應(yīng),與 GatewayFilter 的作用一樣。區(qū)別在于 GlobalFilter 的邏輯可以寫代碼來自定義規(guī)則;而 GatewayFilter 通過配置定義,處理邏輯是固定的。

需求:定義全局過濾器,攔截請(qǐng)求,判斷請(qǐng)求的參數(shù)是否滿足下面條件

  • 參數(shù)中是否有 authorization

  • authorization 參數(shù)值是否為 admin

如果同時(shí)滿足則放行,否則攔截。

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    // 測(cè)試:http://localhost:10010/order/101?authorization=admin
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 獲取第一個(gè) authorization 參數(shù)
        String authorization = exchange.getRequest().getQueryParams().getFirst("authorization");
        if ("admin".equals(authorization)){
            // 放行
            return chain.filter(exchange);
        }
        // 設(shè)置攔截狀態(tài)碼信息
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 設(shè)置攔截
        return exchange.getResponse().setComplete();
    }

    // 設(shè)置過濾器優(yōu)先級(jí),值越低優(yōu)先級(jí)越高
    // 也可以使用 @Order 注解
    @Override
    public int getOrder() {
        return 0;
    }
}

過濾器順序

請(qǐng)求進(jìn)入網(wǎng)關(guān)會(huì)碰到三類過濾器:DefaultFilter、當(dāng)前路由的過濾器、GlobalFilter;

請(qǐng)求路由后,會(huì)將三者合并到一個(gè)過濾器鏈(集合)中,排序后依次執(zhí)行每個(gè)過濾器.


排序的規(guī)則是什么呢?

  • 每一個(gè)過濾器都必須指定一個(gè) int 類型的 order 值,order 值越小,優(yōu)先級(jí)越高,執(zhí)行順序越靠前。
  • GlobalFilter 通過實(shí)現(xiàn) Ordered 接口,或者使用 @Order 注解來指定 order 值,由我們自己指定。
  • 路由過濾器和 defaultFilter 的 order 由 Spring 指定,默認(rèn)是按照聲明順序從1遞增。
  • 當(dāng)過濾器的 order 值一樣時(shí),會(huì)按照 defaultFilter > 路由過濾器 > GlobalFilter 的順序執(zhí)行。

跨域問題

不了解跨域問題的同學(xué)可以百度了解一下;在 Gateway 網(wǎng)關(guān)中解決跨域問題還是比較方便的。

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域處理
        add-to-simple-url-handler-mapping: true # 解決options請(qǐng)求被攔截問題
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允許哪些網(wǎng)站的跨域請(qǐng)求 allowedOrigins: “*” 允許所有網(wǎng)站
              - "http://localhost:8090"
            allowedMethods: # 允許的跨域ajax的請(qǐng)求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允許在請(qǐng)求中攜帶的頭信息
            allowCredentials: true # 是否允許攜帶cookie
            maxAge: 360000 # 這次跨域檢測(cè)的有效期
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容