第五章 Gateway--服務(wù)網(wǎng)關(guān)

接上一篇文章開始網(wǎng)關(guān)之旅蓝角,首先告訴大家網(wǎng)關(guān)是什么径簿,Gateway簡介,怎么配置俘种,怎么入門秤标,執(zhí)行流程等等相關(guān)介紹。

第一章:微服務(wù)的架構(gòu)介紹發(fā)展
第二章:微服務(wù)環(huán)境搭建
第三章:Nacos Discovery--服務(wù)治理
第四章:Sentinel--服務(wù)容錯
第五章:Gateway--服務(wù)網(wǎng)關(guān)
第六章:Sleuth--鏈路追蹤
第七章:Rocketmq--消息驅(qū)動

5.1網(wǎng)關(guān)簡介

大家都都知道在微服務(wù)架構(gòu)中宙刘,一個系統(tǒng)會被拆分為很多個微服務(wù)苍姜。那么作為客戶端要如何去調(diào)用
這么多的微服務(wù)呢?如果沒有網(wǎng)關(guān)的存在悬包,我們只能在客戶端記錄每個微服務(wù)的地址衙猪,然后分別去用。

image

這樣的架構(gòu)布近,會存在著諸多的問題:

客戶端多次請求不同的微服務(wù)垫释,增加客戶端代碼或配置編寫的復(fù)雜性

認(rèn)證復(fù)雜,每個服務(wù)都需要獨立認(rèn)證撑瞧。

存在跨域請求棵譬,在一定場景下處理相對復(fù)雜。

上面的這些問題可以借助API網(wǎng)關(guān)來解決预伺。

所謂的API網(wǎng)關(guān)订咸,就是指系統(tǒng)的統(tǒng)一入口,它封裝了應(yīng)用程序的內(nèi)部結(jié)構(gòu)酬诀,為客戶端提供統(tǒng)一服務(wù)脏嚷,一些與業(yè)務(wù)本身功能無關(guān)的公共邏輯可以在這里實現(xiàn),諸如認(rèn)證料滥、鑒權(quán)然眼、監(jiān)控艾船、路由轉(zhuǎn)發(fā)等等葵腹。

添加上API網(wǎng)關(guān)之后高每,系統(tǒng)的架構(gòu)圖變成了如下所示:

image

我們也可以觀察下,我們現(xiàn)在的整體架構(gòu)圖:

image

在業(yè)界比較流行的網(wǎng)關(guān)践宴,有下面這些:

Ngnix+lua

使用nginx的反向代理和負(fù)載均衡可實現(xiàn)對api服務(wù)器的負(fù)載均衡及高可用

lua是一種腳本語言,可以來編寫一些簡單的邏輯, nginx支持lua腳本

Kong

基于Nginx+Lua開發(fā)鲸匿,性能高,穩(wěn)定阻肩,有多個可用的插件(限流带欢、鑒權(quán)等等)可以開箱即用。問題:只支持Http協(xié)議烤惊;二次開發(fā)乔煞,自由擴展困難;提供管理API柒室,缺乏更易用的管控渡贾、配置方式。

Zuul Netflix

開源的網(wǎng)關(guān)雄右,功能豐富空骚,使用JAVA開發(fā),易于二次開發(fā) 問題:缺乏管控擂仍,無法動態(tài)配置囤屹;依賴組件較多;處理Http請求依賴的是Web容器逢渔,性能不如Nginx

Spring Cloud Gateway

Spring公司為了替換Zuul而開發(fā)的網(wǎng)關(guān)服務(wù)肋坚,將在下面具體介紹。

注意:SpringCloud Alibaba 技術(shù)棧中并沒有提供自己的網(wǎng)關(guān)复局,我們可以采用Spring Cloud Gateway來做網(wǎng)關(guān)

5.2 Gateway 簡介

Spring Cloud Gateway是Spring公司基于Spring 5.0冲簿,Spring Boot 2.0 和 Project Reactor 等技術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的 API 路由管理方式亿昏。它的目標(biāo)是替代Netflix Zuul峦剔,其不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能角钩,例如:安全吝沫,監(jiān)控和限流。

優(yōu)點:

  • 性能強勁:是第一代網(wǎng)關(guān)Zuul的1.6倍
  • 功能強大:內(nèi)置了很多實用的功能递礼,例如轉(zhuǎn)發(fā)惨险、監(jiān)控、限流等
  • 設(shè)計優(yōu)雅脊髓,容易擴展
    缺點:
  • 其實現(xiàn)依賴Netty與WebFlux辫愉,不是傳統(tǒng)的Servlet編程模型,學(xué)習(xí)成本高不能將其部署在Tomcat将硝、Jetty等Servlet容器里恭朗,只能打成jar包執(zhí)行
  • 需要Spring Boot 2.0及以上的版本屏镊,才支持

5.3 Gateway 快速入門

要求: 通過瀏覽器訪問api網(wǎng)關(guān),然后通過網(wǎng)關(guān)將請求轉(zhuǎn)發(fā)到商品微服務(wù)

5.3.1 基礎(chǔ)版

1)創(chuàng)建一個 api-gateway 的模塊, 導(dǎo)入相關(guān)依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <parent>
        <artifactId>micro-mall</artifactId>
        <groupId>com.bxs</groupId>
        <version>1.0.0</version>
    </parent>
    <artifactId>mall-gateway</artifactId>
    <name>${project.artifactId}</name>
    <description>spring cloud gateway 網(wǎng)關(guān)</description>

    <dependencies>
        <!-- gateway 網(wǎng)關(guān) -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
</project>

2)創(chuàng)建主類 @SpringBootApplication

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

3)添加配置文件

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    gateway:
      routes: # 路由數(shù)組[路由 就是指定當(dāng)請求滿足什么條件的時候轉(zhuǎn)到哪個微服務(wù)]
        - id: product_route # 當(dāng)前路由的標(biāo)識, 要求唯一
          uri: http://localhost:8081 # 請求要轉(zhuǎn)發(fā)到的地址
          order: 1 # 路由的優(yōu)先級,數(shù)字越小級別越高
          predicates: # 斷言(就是路由轉(zhuǎn)發(fā)要滿足的條件)
            - Path=/product-serv/** # 當(dāng)請求路徑滿足Path指定的規(guī)則時,才進(jìn)行路由轉(zhuǎn)發(fā)
          filters: # 過濾器,請求在傳遞過程中可以通過過濾器對其進(jìn)行一定的修改
            - StripPrefix=1 # 轉(zhuǎn)發(fā)之前去掉1層路徑 |

4)啟動項目, 并通過網(wǎng)關(guān)去訪問微服務(wù)

image

5.3.2 增強版

1)現(xiàn)在在配置文件中寫死了轉(zhuǎn)發(fā)路徑的地址, 前面我們已經(jīng)分析過地址寫死帶來的問題, 接下來我們從注冊中心獲取此地址。

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

2)在主類上添加注解

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

3)修改配置文件

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      enabled: true
      routes: # 路由數(shù)組[路由 就是指定當(dāng)請求滿足什么條件的時候轉(zhuǎn)到哪個微服務(wù)]
        - id: product_route # 當(dāng)前路由的標(biāo)識, 要求唯一
          uri: http://localhost:8081 # 請求要轉(zhuǎn)發(fā)到的地址
          order: 1 # 路由的優(yōu)先級,數(shù)字越小級別越高
          predicates: # 斷言(就是路由轉(zhuǎn)發(fā)要滿足的條件)
            - Path=/product-serv/** # 當(dāng)請求路徑滿足Path指定的規(guī)則時,才進(jìn)行路由轉(zhuǎn)發(fā)
          filters: # 過濾器,請求在傳遞過程中可以通過過濾器對其進(jìn)行一定的修改
            - StripPrefix=1 # 轉(zhuǎn)發(fā)之前去掉1層路徑 |

4)測試

image

5.3.3 簡寫版

1)去掉關(guān)于路由的配置

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true

2)啟動項目痰腮,并通過網(wǎng)關(guān)去訪問微服務(wù)

image

這時候而芥,就發(fā)現(xiàn)只要按照網(wǎng)關(guān)地址/微服務(wù)/接口的格式去訪問,就可以得到成功響應(yīng)膀值。

5.4 Gateway 核心架構(gòu)

5.4.1 基本概念

路由(Route)gateway 中最基本的組件之一棍丐,表示一個具體的路由信息載體。主要定義了下面的幾個信息:

  • id:路由標(biāo)識符沧踏,區(qū)別于其他 Route
  • uri:路由指向的目的地 uri歌逢,即客戶端請求最終被轉(zhuǎn)發(fā)到的微服務(wù)。
  • order:用于多個 Route 之間的排序翘狱,數(shù)值越小排序越靠前趋翻,匹配優(yōu)先級越高。
  • predicate:斷言的作用是進(jìn)行條件判斷盒蟆,只有斷言都返回真踏烙,才會真正的執(zhí)行路由。
  • filter:過濾器用于修改請求和響應(yīng)信息历等。

5.4.2 執(zhí)行流程執(zhí)行流程大體如下:

image

1)Gateway ClientGateway Server 發(fā)送請求
2)請求首先會被HttpWebHandlerAdapter進(jìn)行提取組裝成網(wǎng)關(guān)上下文
3)然后網(wǎng)關(guān)的上下文會傳遞到 DispatcherHandler讨惩,它負(fù)責(zé)將請求分發(fā)給 RoutePredicateHandlerMapping
4)RoutePredicateHandlerMapping負(fù)責(zé)路由查找,并根據(jù)路由斷言判斷路由是否可用
5)如果過斷言成功寒屯,由FilteringWebHandler創(chuàng)建過濾器鏈并調(diào)用
6)請求會一次經(jīng)過PreFilter微服務(wù)PostFilter的方法荐捻,最終返回響應(yīng)

5.5 斷言

Predicate(斷言, 謂詞) 用于進(jìn)行條件判斷,只有斷言都返回真寡夹,才會真正的執(zhí)行路由

斷言就是說: 在 什么條件下 才能進(jìn)行路由轉(zhuǎn)發(fā)

5.5.1 內(nèi)置路由斷言工廠

Spring Cloud Gateway 包括許多內(nèi)置的斷言工廠处面,所有這些斷言都與HTTP請求的不同屬性匹配。具體如下:

基于Datetime類型的斷言工廠

此類型的斷言根據(jù)時間做判斷菩掏,主要有三個:
1. AfterRoutePredicateFactory: 接收一個日期參數(shù)魂角,判斷請求日期是否晚于指定日期
2. BeforeRoutePredicateFactory: 接收一個日期參數(shù),判斷請求日期是否早于指定日期
3. BetweenRoutePredicateFactory : 接收兩個日期參數(shù)智绸,判斷請求日期是否在指定時間段內(nèi)

-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]

基于遠(yuǎn)程地址的斷言工廠

RemoteAddrRoutePredicateFactory: 接收一個IP地址段野揪,判斷請求主機地址是否在地址段中
-RemoteAddr=192.168.1.1/24

基于Cookie的斷言工廠

CookieRoutePredicateFactory:接收兩個參數(shù),cookie 名字和一個正則表達(dá)式瞧栗。 判斷請求
cookie是否具有給定名稱且值與正則表達(dá)式匹配斯稳。
-Cookie=chocolate, ch.

基于Header的斷言工廠

HeaderRoutePredicateFactory:接收兩個參數(shù),標(biāo)題名稱和正則表達(dá)式迹恐。 判斷請求Header是否具有給定名稱且值與正則表達(dá)式匹配挣惰。
-Header=X-Request-Id, \d+

基于Host的斷言工廠

HostRoutePredicateFactory:接收一個參數(shù),主機名模式。判斷請求的Host是否滿足匹配規(guī)則憎茂。
-Host=.testhost.org**

基于Method請求方法的斷言工廠

MethodRoutePredicateFactory:接收一個參數(shù)唆涝,判斷請求類型是否跟指定的類型匹配。
-Method=GET

基于Path請求路徑的斷言工廠

PathRoutePredicateFactory:接收一個參數(shù)唇辨,判斷請求的URI部分是否滿足路徑規(guī)則。
-Path=/foo/{segment}

基于Query請求參數(shù)的斷言工廠

QueryRoutePredicateFactory :接收兩個參數(shù)能耻,請求param和正則表達(dá)式赏枚, 判斷請求參數(shù)是否具有給定名稱且值與正則表達(dá)式匹配。
-Query=baz, ba.

基于路由權(quán)重的斷言工廠

WeightRoutePredicateFactory:接收一個[組名,權(quán)重], 然后對于同一個組內(nèi)的路由按照權(quán)重轉(zhuǎn)發(fā)
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/
-Weight= group3, 9

內(nèi)置路由斷言工廠的使用

接下來我們驗證幾個內(nèi)置斷言的使用:

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product_route
          uri: lb://service-product
          predicates:
            - Path=/product-serv/**
            - Before=2020-03-28T00:00:00.000+08:00 #限制請求時間在2019-03-28之前
            - Method=POST #限制請求方式為POST
          filters:
            - StripPrefix=1

5.5.2 自定義路由斷言工廠

條件 :我們來設(shè)定一個場景: 假設(shè)我們的應(yīng)用僅僅讓age在(min,max)之間的人來訪問晓猛。

1)配置 bootstrap.yml

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product-route
          uri: lb://product-service
          predicates:
            - Path=/product-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1

2)自定義一個斷言工廠, 實現(xiàn)斷言方法

/**
 * @Author Morse
 * @Date 2020-04-17 17:20
 *
 * 這是一個自定義的路由斷言工廠類饿幅,要求有兩個
 * 1. 名字必須是 配置 + RoutePredicateFactory
 * 2. 必須繼承 AbstractRoutePredicateFactory<配置類>
 */
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    // 構(gòu)造函數(shù)
    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    // 讀取配置文件中的參數(shù)值,給他賦值到配置類中的屬性上
    @Override
    public List<String> shortcutFieldOrder() {
        // 這個位置的順序必須跟配置文件中的值的順序?qū)?yīng)
        return Arrays.asList("minAge", "maxAge");
    }

    // 斷言邏輯
    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                // 1. 接收前臺傳入的age參數(shù)
                String ageStr = exchange.getRequest().getQueryParams().getFirst("age");

                // 2. 先判斷是否為空
                if (!StringUtils.isEmpty(ageStr)) {
                    // 3. 如果不為空戒职,再進(jìn)行路由邏輯判斷
                    int age = Integer.parseInt(ageStr);
                    if (age < config.getMaxAge() && age > config.getMinAge()) {
                        return true;
                    } else {
                        return false;
                    }
                }
                return false;
            }
        };
    }

    @ApiModel(value = "配置類栗恩,用于接收配置文件中的對應(yīng)參數(shù)")
    @Data
    @NoArgsConstructor
    public static class Config {
        private int minAge; //18
        private int maxAge; //60
    }
}

4)啟動測試

測試發(fā)現(xiàn)當(dāng)age在(20,60)可以訪問,其它范圍不能訪問

http://localhost:7000/product-serv/product/1?age=30

http://localhost:7000/product-serv/product/1?age=10

5.6 過濾器

三個知識點:

1. 作用:過濾器就是在請求的傳遞過程中,對請求和響應(yīng)做一些手腳
2. 生命周期:Pre Post
3 分類:局部過濾器(作用在某一個路由上) 全局過濾器(作用全部路由上)

在 Gateway 中, Filter 的生命周期只有兩個:“pre” 和 “post”。

PRE:這種過濾器在請求被路由之前調(diào)用洪燥。我們可利用這種過濾器實現(xiàn)身份驗證磕秤、在集群中選擇請求的微服務(wù)、記錄調(diào)試信息等捧韵。
POST:這種過濾器在路由到微服務(wù)以后執(zhí)行市咆。這種過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的 HTTP Header、收集統(tǒng)計信息和指標(biāo)再来、將響應(yīng)從微服務(wù)發(fā)送給客戶端等蒙兰。

Gateway 的 Filter 從作用范圍可分為兩種:

GatewayFilter:應(yīng)用到單個路由或者一個分組的路由上。
GlobalFilter:應(yīng)用到所有的路由上芒篷。

image

5.6.1 局部過濾器

局部過濾器是針對單個路由的過濾器搜变。

5.6.1.1 內(nèi)置局部過濾器

在 Spring Cloud Gateway 中內(nèi)置了很多不同類型的網(wǎng)關(guān)路由過濾器。具體如下:
相關(guān)鏈接

序號 過濾器工廠 作用 參數(shù)
1 AddRequestHeader 為原始請求添加Header Header的名稱與值
2 AddRequestParameter 為原始請求添加請求參數(shù) 參數(shù)名稱及值
3 AddResponseHeader 為原始響應(yīng)添加Header Header的名稱及值
4 DedupeResponseHeader 剔除響應(yīng)頭中重復(fù)的值 需要去重的Header名稱及去重策略
5 Hystrix 為路由引入Hystrix的斷路器保護(hù) HystrixCommand的名稱
6 CircuitBreaker
7 FallbackHeaders 為fallbackUrl的請求頭中添加具體的異常信息 Header的名稱
8 MapRequestHeader
9 PrefixPath 為原始請求路徑添加前綴 前綴路徑
10 PreserveHostHeader 為請求添加一個 preserveHostHeader=true 的屬性针炉,路由過濾器會檢查該屬性以決定是否要發(fā)送原始的Host
11 RequestRateLimiter 用于對請求限流挠他,限流算法為令牌桶 keyResolver、rateLimiter篡帕、statusCode绩社、denyEmptyKey、emptyKeyStatus
12 RedirectTo 將原始請求重定向到指定的URL http狀態(tài)碼及重定向的url
13 RemoveResponseHeader 為原始請求刪除某個Header Header名稱
14 RemoveResponseHeader 為原始響應(yīng)刪除某個Header Header名稱
15 RemoveRequestParameter 為原始請求刪除某個Parameter Parameter名稱
16 RewritePath 重寫原始的請求路徑 原始路徑正則表達(dá)以及重寫路徑的正則表達(dá)式
17 RewriteLocationResponseHeader
18 RewriteResponseHeader 重寫原始響應(yīng)中的某個Header Header名稱赂苗,值的正則表達(dá)式愉耙,重寫后的值
19 SaveSession 在轉(zhuǎn)發(fā)請求之前,強制執(zhí)行 WebSession::save 操作
20 SecureHeaders 為原始響應(yīng)添加一系列起安全作用的響應(yīng)頭 無拌滋,支持修改這些安全響應(yīng)頭的值
21 SetPath 修改原始的請求路徑 修改后的路徑
22 SetRequestHeader 修改原始請求中某個Header的值 Header名稱朴沿,修改后的值
23 SetResponseHeader 修改原始響應(yīng)中某個Header的值 Header名稱,修改后的值
24 SetStatus 修改原始響應(yīng)的狀態(tài)碼 HTTP狀態(tài)碼,可以是數(shù)字赌渣,也可以是字符串
25 StripPrefix 用于階段原始請求的路徑 使用數(shù)字表示要截斷的路徑的數(shù)量
26 Retry 針對不同的響應(yīng)進(jìn)行重試 retries魏铅、statuses、methods坚芜、series览芳、exceptions、backoff
27 RequestSize 設(shè)置允許接收最大請求包的大小鸿竖。如果請求包大小超過設(shè)置的值沧竟,則返回 413 Payload Too Large 請求包大小,單位為字節(jié)缚忧,默認(rèn)值為5M
28 ModifyRequestBody 在轉(zhuǎn)發(fā)請求之前修改原始請求體內(nèi)容 修改后的請求體內(nèi)容
29 ModifyResponseBody 修改原始響應(yīng)體的內(nèi)容 修改后的響應(yīng)體內(nèi)容
30 Default 為所有路由添加過濾器 過濾器工廠名稱及值

內(nèi)置局部過濾器的使用

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product-route
          uri: lb://product-service
          predicates:
            - Path=/product-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1
            - SetStatus=2000 # 修改返回狀態(tài)

5.6.1.2 自定義局部過濾器

1)在配置文件中,添加一個Log的過濾器配置

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: consumer
          uri: lb://consumer
          predicates:
            - Path=/consumer-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1
            - Log=true,false # 修改返回狀態(tài)

2)自定義一個過濾器工廠,實現(xiàn)方法

@Slf4j                                                                                                    
@Component                                                                                                
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> 
                                                                                                          
    //構(gòu)造函數(shù)                                                                                                
    public LogGatewayFilterFactory() {                                                                    
        super(LogGatewayFilterFactory.Config.class);                                                      
    }                                                                                                     
                                                                                                          
    //讀取配置文件中的參數(shù) 賦值到 配置類中                                                                                 
    @Override                                                                                             
    public List<String> shortcutFieldOrder() {                                                            
        return Arrays.asList("consoleLog", "cacheLog");                                                   
    }                                                                                                     
                                                                                                          
    //過濾器邏輯                                                                                               
    @Override                                                                                             
    public GatewayFilter apply(Config config) {                                                           
        return new GatewayFilter() {                                                                      
            @Override                                                                                     
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {              
                if (config.isCacheLog()) {                                                                
                    System.out.println("cacheLog已經(jīng)開啟了......");                                            
                }                                                                                         
                if (config.isConsoleLog()) {                                                              
                    System.out.println("consoleLog已經(jīng)開啟了......");                                          
                }                                                                                         
                return chain.filter(exchange);                                                            
            }                                                                                             
        };                                                                                                
    }                                                                                                     
                                                                                                          
    // 配置類 接收配置參數(shù)                                                                                         
    @Data                                                                                                 
    @NoArgsConstructor                                                                                    
    public static class Config {                                                                          
        private boolean consoleLog;                                                                       
        private boolean cacheLog;                                                                         
    }                                                                                                     
}

3)啟動測試

5.6.2 全局過濾器

全局過濾器作用于所有路由, 無需配置悟泵。通過全局過濾器可以實現(xiàn)對權(quán)限的統(tǒng)一校驗,安全性驗證等功能闪水。

5.6.2.1 內(nèi)置全局過濾器

Spring Cloud Gateway 內(nèi)部也是通過一系列的內(nèi)置全局過濾器對整個路由轉(zhuǎn)發(fā)進(jìn)行處理如下:

image

5.6.2.2 自定義全局過濾器

內(nèi)置的過濾器已經(jīng)可以完成大部分的功能糕非,但是對于企業(yè)開發(fā)的一些業(yè)務(wù)功能處理,還是需要我們自己編寫過濾器來實現(xiàn)的球榆,那么我們一起通過代碼的形式自定義一個過濾器朽肥,去完成統(tǒng)一的權(quán)限校驗。

開發(fā)中的鑒權(quán)邏輯:
1)當(dāng)客戶端第一次請求服務(wù)時持钉,服務(wù)端對用戶進(jìn)行信息認(rèn)證(登錄)

2)認(rèn)證通過鞠呈,將用戶信息進(jìn)行加密形成token,返回給客戶端右钾,作為登錄憑證

3)以后每次請求蚁吝,客戶端都攜帶認(rèn)證的token

4)服務(wù)端對token進(jìn)行解密,判斷是否有效舀射。

image

如上圖窘茁,對于驗證用戶是否已經(jīng)登錄鑒權(quán)的過程可以在網(wǎng)關(guān)統(tǒng)一檢驗。檢驗的標(biāo)準(zhǔn)就是請求中是否攜帶token憑證以及token的正確性脆烟。

下面的我們自定義一個GlobalFilter山林,去校驗所有請求的請求參數(shù)中是否包含token,如何不包含請求參數(shù)token則不轉(zhuǎn)發(fā)路由邢羔,否則執(zhí)行正常的邏輯驼抹。

/**
 * @Author Morse
 * @Date 2020-04-18 09:51
 *
 * 自定義全局過濾器需要實現(xiàn) GlobalFilter 和 Ordered 接口
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    // 完成判斷邏輯
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (!StringUtils.equals( token, "admin")) {
            System.out.println("健全失敗");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        //  調(diào)用chain.filter繼續(xù)向下游執(zhí)行
        return chain.filter(exchange);
    }

    // 順序,數(shù)值越小拜鹤,優(yōu)先級越高
    @Override
    public int getOrder() {
        return 0;
    }
}

5.7 網(wǎng)關(guān)限流

網(wǎng)關(guān)是所有請求的公共入口框冀,所以可以在網(wǎng)關(guān)進(jìn)行限流,而且限流的方式也很多敏簿,我們本次采用前面學(xué)過的Sentinel組件來實現(xiàn)網(wǎng)關(guān)的限流明也。Sentinel支持對Spring Cloud Gateway宣虾、Zuul等主流網(wǎng)關(guān)進(jìn)行限流。

image

從1.6.0版本開始温数,Sentinel提供了Spring Cloud Gateway的適配模塊绣硝,可以提供兩種資源維度的限流:
route維度:即在Spring配置文件中配置的路由條目,資源名為對應(yīng)的routeId
自定義API維度:用戶可以利用Sentinel提供的API來自定義一些API分組

1)導(dǎo)入依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

2)編寫配置類

基于 Sentinel 的 Gateway 限流是通過其提供的 Filter 來完成的撑刺,使用時只需注入對應(yīng)的 SentinelGatewayFilter 實例以及 SentinelGatewayBlockExceptionHandler 實例即可鹉胖。

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    // 初始化一個限流的過濾器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    // 配置初始化的限流參數(shù)
    @PostConstruct
    public void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                // 資源名稱,對應(yīng)路由ID
                new GatewayFlowRule("product-route")
                        // 限流閥值
                        .setCount(1)
                        // 統(tǒng)計時間窗口够傍,單位是秒甫菠,默認(rèn)是1秒
                        .setIntervalSec(1)
        );
        GatewayRuleManager.loadRules(rules);
    }

    // 配置限流的異常處理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 自定義限流異常頁面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

}

3)測試

在一秒鐘內(nèi)多次訪問http://localhost:7000/product-serv/product/1就可以看到限流啟作用了。

4)自定義API分組

自定義API分組是一種更細(xì)粒度的限流規(guī)則定義

    // 配置初始化的限流參數(shù)
    @PostConstruct
    public void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                // 資源名稱王带,對應(yīng)路由ID
                new GatewayFlowRule("product-route")
                        // 限流閥值
                        .setCount(1)
                        // 統(tǒng)計時間窗口,單位是秒市殷,默認(rèn)是1秒
                        .setIntervalSec(1)
        );
        rules.add(new GatewayFlowRule("product-api1").setCount(1) .setIntervalSec(1));
        rules.add(new GatewayFlowRule("product-api2").setCount(1) .setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    //  自定義API分組
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("product-api1")
                .setPredicateItems(
                        new HashSet<ApiPredicateItem>() {{
                                // 以/product-serv/product/api1 開頭的請求
                                add(
                                        new ApiPathPredicateItem()
                                                .setPattern("/product-serv/product/api1/**")
                                                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)
                                );
                        }}
                );
        ApiDefinition api2 = new ApiDefinition("product-api2")
                .setPredicateItems(
                        new HashSet<ApiPredicateItem>(){{
                                add( new ApiPathPredicateItem().setPattern("/product-serv/product/api2/demo1"));
                        }}
                );
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愕撰,一起剝皮案震驚了整個濱河市碍粥,隨后出現(xiàn)的幾起案子逃顶,更是在濱河造成了極大的恐慌,老刑警劉巖磕蛇,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件音羞,死亡現(xiàn)場離奇詭異囱桨,居然都是意外死亡,警方通過查閱死者的電腦和手機嗅绰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門舍肠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窘面,你說我怎么就攤上這事翠语。” “怎么了财边?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵肌括,是天一觀的道長。 經(jīng)常有香客問我酣难,道長谍夭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任憨募,我火速辦了婚禮紧索,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菜谣。我一直安慰自己齐板,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甘磨,像睡著了一般橡羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上济舆,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天卿泽,我揣著相機與錄音,去河邊找鬼滋觉。 笑死签夭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的椎侠。 我是一名探鬼主播第租,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼我纪!你這毒婦竟也來了慎宾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤浅悉,失蹤者是張志新(化名)和其女友劉穎趟据,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體术健,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡汹碱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荞估。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咳促。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖勘伺,靈堂內(nèi)的尸體忽然破棺而出等缀,到底是詐尸還是另有隱情,我是刑警寧澤娇昙,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布尺迂,位于F島的核電站,受9級特大地震影響冒掌,放射性物質(zhì)發(fā)生泄漏噪裕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一股毫、第九天 我趴在偏房一處隱蔽的房頂上張望膳音。 院中可真熱鬧,春花似錦铃诬、人聲如沸祭陷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兵志。三九已至醇蝴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間想罕,已是汗流浹背悠栓。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留按价,地道東北人惭适。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像楼镐,于是被迫代替她去往敵國和親癞志。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容