Spring Cloud Gateway - 擴(kuò)展

[TOC]


Spring Cloud Gateway監(jiān)控端點(diǎn)

說到監(jiān)控萧朝,就應(yīng)該能想到Spring Boot Actuator白筹。而Spring Cloud Gateway基于Actuator提供了許多的監(jiān)控端點(diǎn)徒河。只需要在項(xiàng)目中添加spring-boot-starter-actuator依賴,并將 gateway 端點(diǎn)暴露棒厘,即可獲得若干監(jiān)控端點(diǎn)。配置示例:

management:
  endpoints:
    web:
      exposure:
        include: gateway  # 或者配置“*”暴露全部端點(diǎn)

Spring Cloud Gateway的監(jiān)控端點(diǎn)如下表:

端點(diǎn) 請求方法 描述
globalfilters GET 展示所有的全局過濾器信息
routefilters GET 展示所有的過濾器工廠信息
refresh POST(無消息體) 清空路由緩存,即刷新路由信息
routes GET 展示所有的路由信息列表
routes/{id} GET 展示指定id的路由的信息
routes/{id} POST(有消息體) 新增一個(gè)路由
routes/{id} DELETE(無消息體) 刪除一個(gè)路由

Gateway所有的監(jiān)控端點(diǎn)都掛載在 /actuator/gateway 路徑下支救,例如globalfilters端點(diǎn)的完整訪問路徑是 /actuator/gateway/globalfilters各墨。該端點(diǎn)主要是查看Gateway啟用了哪些全局過濾器以及它們的執(zhí)行順序(數(shù)字越小越優(yōu)先執(zhí)行)贬堵。所以當(dāng)我們不知道Gateway啟用了哪些全局過濾器黎做,或者不知道這些全局過濾器的執(zhí)行順序蒸殿,就可以訪問該端點(diǎn)進(jìn)行查看:

image.png

同理酥艳,如果不知道Gateway啟用了哪些過濾器工廠,則可以訪問routefilters端點(diǎn)查看:

image.png

若想知道Gateway里定義了哪些路由而又不想查看配置文件的話浓镜,那么就可以通過routes端點(diǎn)查看所有的路由信息列表:

image.png

如果出現(xiàn)自定義的路由配置不生效或行為與預(yù)期不符,那么可以通過routes/{id}端點(diǎn)查看該路由具體的詳細(xì)信息:

image.png

routes/{id}端點(diǎn)還可以用于動態(tài)添加路由雅任,只需發(fā)送POST請求并定義一個(gè)消息體即可咨跌。消息體示例:

{
  "predicates": [
    {
      "name": "Path",
      "args": {
        "_genkey_0": "/test"
      }
    }
  ],
  "filters": [
    {
      "name": "AddRequestHeader",
      "args": {
        "_genkey_0": "X-Request-Foo",
        "_genkey_1": "Bar"
      }
    },
    {
      "name": "PreLog",
      "args": {
        "_genkey_0": "a",
        "_genkey_1": "b"
      }
    }
  ],
  "uri": "https://www.example.com",
  "order": 0
}

消息體其實(shí)是有規(guī)律的,你可以先在配置文件中配置一個(gè)路由規(guī)則禽车,然后訪問 routes 端點(diǎn),route_definition字段里的內(nèi)容就是消息體殉摔,如下:

image.png

接下來我們實(shí)際測試一下,復(fù)制該消息體记焊,然后稍微修改一下并進(jìn)行發(fā)送逸月,如下:


image.png

路由添加成功后腐缤,訪問 routes 端點(diǎn)岭粤,就可以看到新添加的路由:

image.png

注:如果沒有實(shí)時(shí)生效剃浇,使用 refresh 端點(diǎn)刷新一下路由信息即可

官方文檔:


關(guān)于Gateway的調(diào)試、排錯(cuò)

1蔫磨、Gateway的監(jiān)控端點(diǎn):

上一小節(jié)介紹了Gateway的監(jiān)控端點(diǎn)淘讥,這些監(jiān)控端點(diǎn)可以幫助我們分析全局過濾器、過濾器工廠堤如、路由詳情等

2蒲列、日志:

設(shè)置一些相關(guān)包的日志級別,打印更詳細(xì)的日志信息搀罢,可按需將如下包的日志級別設(shè)置成 debugtrace

  • org.springframework.cloud.gateway
  • org.springframework.http.server.reactive
  • org.springframework.web.reactive
  • org.springframework.boot.autoconfigure.web
  • reactor.netty
  • redisratelimiter

配置示例:

logging:
  level:
    org.springframework.cloud.gateway: trace

3蝗岖、Wiretap Logger【需Greenwich SR3及更高版本才會支持】:

Reactor Netty的 HttpClient 以及 HttpServer 可啟用 Wiretap。需將 reactor.netty 包設(shè)置成 debugtrace 榔至,然后在配置文件中添加如下配置:

  • spring.cloud.gateway.httpserver.wiretap=true:開啟 HttpServer 的Wiretap
  • spring.cloud.gateway.httpclient.wiretap=true:開啟 HttpClient 的Wiretap

wiretap其實(shí)是Reactor Netty的概念抵赢,用于打印對端之間的流量詳情,相關(guān)文檔:


過濾器執(zhí)行順序

我們都知道全局過濾器使用@Order 注解或?qū)崿F(xiàn) Ordered 接口來配置一個(gè)決定執(zhí)行順序的數(shù)字唧取,該數(shù)字越小的過濾器越靠前執(zhí)行铅鲤。

但是在路由規(guī)則上所配置的過濾器工廠并沒有配置類似Order之類的東西,那么是如何決定執(zhí)行順序的呢枫弟?其實(shí)邢享,過濾器工廠默認(rèn)也會被設(shè)置一個(gè)Order,該Order按配置順序從1開始遞增媒区,也是Order越小越靠前執(zhí)行驼仪。如下:

routes:
  - id: test-route
    uri: lb://user-center
    predicates:
      - TimeBetween=上午9:00,下午5:00
    filters:
      # 按配置順序從1開始遞增
      - AddRequestHeader=Y-Header, Bar    # Order為1
      - AddResponseHeader=X-Header, Bar   # Order為2
      - PreLog=testName,testValue         # Order為3

使用default-filters配置的默認(rèn)過濾器也是同理,但如果配置了默認(rèn)過濾器袜漩,則會先執(zhí)行相同Order的默認(rèn)過濾器:

default-filters: 
  - AddRequestHeader=Y-Foo, Bar    # Order為1
  - AddResponseHeader=X-Foo, Bar   # Order為2
routes:
  - id: test-route
    uri: lb://user-center
    predicates:
      - TimeBetween=上午9:00,下午5:00
    filters:
      # 按配置順序從1開始遞增
      - AddRequestHeader=Y-Header, Bar    # Order為1
      - AddResponseHeader=X-Header, Bar   # Order為2
      - PreLog=testName,testValue         # Order為3

如需自行控制過濾器工廠的Order绪爸,可返回OrderedGatewayFilter,如下示例:

@Slf4j
@Component
public class PreLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public OrderedGatewayFilter apply(NameValueConfig config) {
        return new OrderedGatewayFilter((exchange, chain) -> {
            ...
            return chain.filter(exchange);
        }, -1);
    }
}

核心代碼:

  • org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#loadGatewayFilters:為過濾器設(shè)置了Order 數(shù)值宙攻,從1開始
  • org.springframework.cloud.gateway.route. RouteDefinitionRouteLocator#getFilters:加載默認(rèn)過濾器 & 路由過濾器奠货,并對過濾器做了排序
  • org.springframework.cloud.gateway.handler.FilteringWebH .andler#handle:構(gòu)建過濾器鏈并執(zhí)行

Spring Cloud Gateway跨域配置

Gateway支持CORS相關(guān)配置,可以通過不同的URL規(guī)則匹配不同的CORS策略座掘。配置示例:

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "http://docs.spring.io"
            allowedMethods:
            - GET

除此之外递惋,還可以通過自定義過濾器來解決跨域問題柔滔,具體代碼如下:

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;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

Spring Cloud Gateway限流相關(guān)

在高并發(fā)的系統(tǒng)中,限流往往是一個(gè)繞不開的話題萍虽,我們都知道網(wǎng)關(guān)是流量的入口睛廊,所以在網(wǎng)關(guān)上做限流也是理所當(dāng)然的。Spring Cloud Gateway內(nèi)置了一個(gè)過濾器工廠杉编,用于提供限流功能超全,這個(gè)過濾器工廠就是是RequestRateLimiterGatewayFilterFactory,該過濾器工廠基于令牌桶算法實(shí)現(xiàn)限流功能邓馒。

目前嘶朱,該過濾器工廠默認(rèn)使用 RedisRateLimiter 作為限速器,需要依賴Redis來存儲限流配置光酣,以及統(tǒng)計(jì)數(shù)據(jù)等疏遏。當(dāng)然你也可以實(shí)現(xiàn)自己的RateLimiter,只需實(shí)現(xiàn) org.springframework.cloud.gateway.filter.ratelimit.RateLimiter 接口救军,或者繼承 org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter抽象類

Tips:

Redis Rate Limiter的實(shí)現(xiàn)基于這篇文章:Scaling your API with rate limiters
Spring官方引用的令牌桶算法文章:Token bucket

關(guān)于令牌桶之類的限流算法可以參考另一篇文章财异,這里就不過多贅述了:


動手實(shí)踐

1、添加Redis依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2缤言、添加如下配置:

spring:
  cloud:
    gateway:
      routes:
        - id: user-center
          uri: lb://user-center
          predicates:
            - Path=/user-center/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 2
                # 使用SpEL表達(dá)式從Spring容器中獲取Bean對象
                key-resolver: "#{@pathKeyResolver}"
  # redis相關(guān)              
  redis:
    host: 127.0.0.1
    port: 6379

3宝当、編寫一個(gè)KeyResolver,用于定義針對什么進(jìn)行限流胆萧。例如按照訪問路徑限流,就寫一個(gè)針對訪問路徑的KeyResolver俐东;按照請求參數(shù)限流跌穗,那就寫一個(gè)針對請求參數(shù)的KeyResolver,以此類推虏辫。這里我們按照訪問路徑限流蚌吸,具體實(shí)現(xiàn)代碼如下:

@Configuration
public class RaConfiguration {

    /**
     * 按照Path限流
     *
     * @return key
     */
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(
                exchange.getRequest()
                        // 獲取path
                        .getPath()
                        .toString()
        );
    }
}

從代碼的實(shí)現(xiàn)不難看出,實(shí)際就只是返回了一個(gè)訪問路徑砌庄,這樣限流規(guī)則就會作用到訪問路徑上羹唠。例如訪問:http://${GATEWAY_URL}/users/1,對于這個(gè)路徑娄昆,它的redis-rate-limiter.replenishRate = 1佩微,redis-rate-limiter.burstCapacity = 2

訪問:http://${GATEWAY_URL}/shares/1萌焰,對于這個(gè)路徑哺眯,它的redis-rate-limiter.replenishRate = 1redis-rate-limiter.burstCapacity = 2扒俯;以此類推......

測試

接下來進(jìn)行一個(gè)簡單的測試奶卓,看看限流是否起作用了一疯。持續(xù)頻繁地訪問某個(gè)路徑,當(dāng)令牌桶的令牌被消耗完了夺姑,就會返回 429 這個(gè)HTTP狀態(tài)碼墩邀。如下:

image.png

然后迅速查看Redis中存儲的key,會發(fā)現(xiàn)其格式如下:


image.png

從key的格式可以看出來盏浙,實(shí)際上 KeyResolver 的目的就是用來獲取一個(gè)請求的唯一標(biāo)識(這個(gè)標(biāo)識可以是訪問路徑眉睹,可以是某個(gè)請求參數(shù),總之就是可以從這個(gè)請求里獲取出來的東西)只盹,并用其生成key以及解析key辣往,以此實(shí)現(xiàn)針對性的限流。

拓展

如果請求會攜帶一個(gè)名為user的參數(shù)殖卑,其值為用戶名站削,那么我們就可以通過這個(gè)請求參數(shù)來實(shí)現(xiàn)針對用戶的限流。如下:

@Bean
public KeyResolver userKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest()
            .getQueryParams()
            .getFirst("user")
    );
}

同理孵稽,我們還可以針對請求的來源IP進(jìn)行限流许起。如下:

@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
      exchange.getRequest()
          .getHeaders()
          .getFirst("X-Forwarded-For")
    );
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市菩鲜,隨后出現(xiàn)的幾起案子园细,更是在濱河造成了極大的恐慌,老刑警劉巖接校,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猛频,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛛勉,警方通過查閱死者的電腦和手機(jī)鹿寻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诽凌,“玉大人毡熏,你說我怎么就攤上這事÷滤校” “怎么了痢法?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杜顺。 經(jīng)常有香客問我财搁,道長,這世上最難降的妖魔是什么哑舒? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任妇拯,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘越锈。我一直安慰自己仗嗦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布甘凭。 她就那樣靜靜地躺著稀拐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丹弱。 梳的紋絲不亂的頭發(fā)上德撬,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機(jī)與錄音躲胳,去河邊找鬼蜓洪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坯苹,可吹牛的內(nèi)容都是我干的隆檀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粹湃,長吁一口氣:“原來是場噩夢啊……” “哼恐仑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起为鳄,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤裳仆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后孤钦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歧斟,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年偏形,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了构捡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壳猜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滑凉,到底是詐尸還是另有隱情统扳,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布畅姊,位于F島的核電站咒钟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏若未。R本人自食惡果不足惜朱嘴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萍嬉,春花似錦乌昔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至行冰,卻和暖如春溺蕉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悼做。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工疯特, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肛走。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓漓雅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羹与。 傳聞我的和親對象是個(gè)殘疾皇子故硅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

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