深入Java微服務(wù)之網(wǎng)關(guān)系列3: SpringCloudalibaba gateway詳解(史上最全)

九男公、服務(wù)網(wǎng)關(guān):Gateway

9.1、網(wǎng)關(guān)簡(jiǎn)介

大家都都知道在微服務(wù)架構(gòu)中耘斩,一個(gè)系統(tǒng)會(huì)被拆分為很多個(gè)微服務(wù)沼填。那么作為客戶端要如何去調(diào)用這么多的微服務(wù)呢?如果沒(méi)有網(wǎng)關(guān)的存在括授,我們只能在客戶端記錄每個(gè)微服務(wù)的地址坞笙,然后分別去調(diào)用。
這樣的架構(gòu)會(huì)存在許多的問(wèn)題:
  1. 客戶端多次請(qǐng)求不同的微服務(wù)荚虚,增加客戶端代碼或配置編寫(xiě)的復(fù)雜性薛夜。

  2. 認(rèn)證復(fù)雜,每個(gè)服務(wù)都需要獨(dú)立認(rèn)證曲管。

  3. 存在跨域請(qǐng)求却邓,在一定場(chǎng)景下處理相對(duì)復(fù)雜。

    網(wǎng)關(guān)就是為了解決這些問(wèn)題而生的院水。所謂的API網(wǎng)關(guān)腊徙,就是指系統(tǒng)的統(tǒng)一入口简十,它封裝了應(yīng)用程序的內(nèi)部結(jié)構(gòu),為客戶端提供統(tǒng)一服務(wù)撬腾,一些與業(yè)務(wù)本身功能無(wú)關(guān)的公共邏輯可以在這里實(shí)現(xiàn)螟蝙,諸如認(rèn)證、鑒權(quán)民傻、監(jiān)控胰默、路由轉(zhuǎn)發(fā)等等。

9.2漓踢、常用的網(wǎng)關(guān)

9.2.1牵署、Ngnix+lua

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

lua是一種腳本語(yǔ)言,可以來(lái)編寫(xiě)一些簡(jiǎn)單的邏輯, nginx支持lua腳本

9.2.2喧半、Kong

基于Nginx+Lua開(kāi)發(fā)奴迅,性能高,穩(wěn)定挺据,有多個(gè)可用的插件(限流取具、鑒權(quán)等等)可以開(kāi)箱即用。

他的缺點(diǎn):

  1. 只支持Http協(xié)議扁耐。
  2. 二次開(kāi)發(fā)暇检,自由擴(kuò)展困難。
  3. 提供管理API婉称,缺乏更易用的管控块仆、配置方式。

9.2.3酿矢、Zuul

Netflix開(kāi)源的網(wǎng)關(guān)榨乎,功能豐富,使用JAVA開(kāi)發(fā)瘫筐,易于二次開(kāi)發(fā)蜜暑。

他的缺點(diǎn):
  1. 缺乏管控,無(wú)法動(dòng)態(tài)配置策肝。
  2. 依賴組件較多肛捍。
  3. 處理Http請(qǐng)求依賴的是Web容器,性能不如Nginx之众。

9.2.4拙毫、Spring Cloud Gateway

Spring公司為了替換Zuul而開(kāi)發(fā)的網(wǎng)關(guān)服務(wù),SpringCloud alibaba技術(shù)棧中并沒(méi)有提供自己的網(wǎng)關(guān)棺禾,我們可以采用Spring Cloud Gateway來(lái)做網(wǎng)關(guān)

9.3缀蹄、Gateway簡(jiǎn)介

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

他的主要功能是:
  1. 進(jìn)行轉(zhuǎn)發(fā)重定向逝段。
  2. 在開(kāi)始的時(shí)候垛玻,所有類都需要做的初始化操作。
  3. 進(jìn)行網(wǎng)絡(luò)隔離奶躯。

9.4帚桩、快速入門(mén)

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

9.4.1嘹黔、基礎(chǔ)版

創(chuàng)建一個(gè)api-gateway 模塊朗儒,并且導(dǎo)入下面的依賴。

<?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">
  <parent>
    Shop-parent
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  api-gateway

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

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

</project>
復(fù)制代碼

編寫(xiě)配置文件

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

測(cè)試

9.4.2参淹、升級(jí)版

我們發(fā)現(xiàn)升級(jí)版有一個(gè)很大的問(wèn)題,那就是在配置文件中寫(xiě)死了轉(zhuǎn)發(fā)路徑的地址乏悄,我們需要在注冊(cè)中心來(lái)獲取地址浙值。

加入nacos依賴

<?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">
  <parent>
    Shop-parent
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  api-gateway

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!--gateway網(wǎng)關(guān)-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      spring-cloud-starter-gateway
    </dependency>
    <!--nacos客戶端-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      spring-cloud-starter-alibaba-nacos-discovery
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      lombok
    </dependency>
  </dependencies>

</project>
復(fù)制代碼

在主類上添加注解

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayServerApp {
  public static void main(String[] args) {
    SpringApplication.run(GateWayServerApp.class,args);
  }
}
復(fù)制代碼

修改配置文件

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù)
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務(wù),并遵循負(fù)載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個(gè)規(guī)定的才進(jìn)行1轉(zhuǎn)發(fā)
          filters:
            - StripPrefix=1 # 將第一層去掉
復(fù)制代碼
我們還可以自定義多個(gè)路由規(guī)則。
spring:
  application:
    gateway:
      routes:
        - id: product_route
          uri: lb://product-service 
          predicates:
            - Path=/product-serv/**
          filters:
            - StripPrefix=1
        - id: order_route
          uri: lb://order-service 
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
復(fù)制代碼

9.4.3檩小、簡(jiǎn)寫(xiě)版

我們的配置文件無(wú)需寫(xiě)的1那么復(fù)雜就可以實(shí)現(xiàn)功能开呐,有一個(gè)簡(jiǎn)寫(xiě)版。
server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù)
復(fù)制代碼
我們發(fā)現(xiàn)规求,就發(fā)現(xiàn)只要按照網(wǎng)關(guān)地址/微服務(wù)名稱/接口的格式去訪問(wèn)筐付,就可以得到成功響應(yīng)。

9.5阻肿、Gateway核心架構(gòu)

9.5.1瓦戚、基本概念

路由(Route) 是 gateway 中最基本的組件之一,表示一個(gè)具體的路由信息載體丛塌。主要定義了下面的幾個(gè)信息:
  1. id:路由標(biāo)識(shí)符较解,區(qū)別于其他 Route。
  2. uri:路由指向的目的地 uri赴邻,即客戶端請(qǐng)求最終被轉(zhuǎn)發(fā)到的微服務(wù)印衔。
  3. order:用于多個(gè) Route 之間的排序,數(shù)值越小排序越靠前姥敛,匹配優(yōu)先級(jí)越高奸焙。
  4. predicate:斷言的作用是進(jìn)行條件判斷,只有斷言都返回真,才會(huì)真正的執(zhí)行路由与帆。
  5. filter:過(guò)濾器用于修改請(qǐng)求和響應(yīng)信息了赌。
  6. predicate:斷言,用于進(jìn)行條件判斷鲤桥,只有斷言都返回真揍拆,才會(huì)真正的執(zhí)行路由。

9.5.2茶凳、執(zhí)行原理

  1. 接收用戶的請(qǐng)求嫂拴,請(qǐng)求處理器交給處理器映射器,返回執(zhí)行鏈贮喧。
  2. 請(qǐng)求處理器去調(diào)用web處理器筒狠,在web處理器里面對(duì)我們的路徑1進(jìn)行處理。假設(shè)1我們的路徑1是:http://localhost:9000/product-serv/get?id=1 箱沦,根據(jù)配置的路由規(guī)則辩恼,上本地找對(duì)應(yīng)的服務(wù)信息:product-service對(duì)應(yīng)的主機(jī)ip是192.168.10.130。
  3. 根據(jù)1ribbon的負(fù)載均衡策略去選擇一個(gè)節(jié)點(diǎn)谓形,然后拼接好灶伊,將路徑中的product-serv替換成192.168.10.130:8081,如果你配置了filter寒跳,那么他還會(huì)走filter聘萨。
  4. 如果你沒(méi)有自定義路由的話,默認(rèn)Gateway會(huì)幫你把第一層去掉童太。網(wǎng)關(guān)端口從此一個(gè)/開(kāi)始到第二個(gè)/開(kāi)始算第一層米辐。

9.6、過(guò)濾器

Gateway的過(guò)濾器的作用是:是在請(qǐng)求的傳遞過(guò)程中,對(duì)請(qǐng)求和響應(yīng)做一些手腳书释。

Gateway的過(guò)濾器的生命周期:
  1. PRE:這種過(guò)濾器在請(qǐng)求被路由之前調(diào)用翘贮。我們可利用這種過(guò)濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇 請(qǐng)求的微服務(wù)爆惧、記錄調(diào)試信息等狸页。

  2. POST:這種過(guò)濾器在路由到微服務(wù)以后執(zhí)行械蹋。這種過(guò)濾器可用來(lái)為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header慢显、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端等乏德。

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

  3. GatewayFilter:應(yīng)用到單個(gè)路由或者一個(gè)分組的路由上叔收。

  4. GlobalFilter:應(yīng)用到所有的路由上齿穗。

9.6.1、局部過(guò)濾器

局部過(guò)濾器是針對(duì)單個(gè)路由的過(guò)濾器饺律。他分為內(nèi)置過(guò)濾器和自定義過(guò)濾器窃页。

9.6.1.1、內(nèi)置過(guò)濾器

在SpringCloud Gateway中內(nèi)置了很多不同類型的網(wǎng)關(guān)路由過(guò)濾器。
9.6.1.1.1脖卖、局部過(guò)濾器內(nèi)容
過(guò)濾器工廠 作用 參數(shù)
AddRequestHeader 為原始請(qǐng)求添加Header Header的名稱及值
AddRequestParameter 為原始請(qǐng)求添加請(qǐng)求參數(shù) 參數(shù)名稱及值
AddResponseHeader 為原始響應(yīng)添加Header Header的名稱及值
DedupeResponseHeader 剔除響應(yīng)頭中重復(fù)的值 需要去重的Header名稱及去重策略
Hystrix 為路由引入Hystrix的斷路器保護(hù) HystrixCommand 的名稱
FallbackHeaders 為fallbackUri的請(qǐng)求頭中添加具體的異常信息 Header的名稱
PrefixPath 為原始請(qǐng)求路徑添加前綴 前綴路徑
PreserveHostHeader 為請(qǐng)求添加一個(gè)preserveHostHeader=true的屬性乒省,路由過(guò)濾器會(huì)檢查該屬性以決定是否要發(fā)送原始的Host 無(wú)
RequestRateLimiter 用于對(duì)請(qǐng)求限流,限流算法為令牌桶 keyResolver畦木、rateLimiter袖扛、statusCode、denyEmptyKey十籍、emptyKeyStatus
RedirectTo 將原始請(qǐng)求重定向到指定的URL http狀態(tài)碼及重定向的url
RemoveHopByHopHeadersFilter 為原始請(qǐng)求刪除IETF組織規(guī)定的一系列Header 默認(rèn)就會(huì)啟用蛆封,可以通過(guò)配置指定僅刪除哪些Header
RemoveRequestHeader 為原始請(qǐng)求刪除某個(gè)Header Header名稱
RemoveResponseHeader 為原始響應(yīng)刪除某個(gè)Header Header名稱
RewritePath 重寫(xiě)原始的請(qǐng)求路徑 原始路徑正則表達(dá)式以及重寫(xiě)后路徑的正則表達(dá)式
RewriteResponseHeader 重寫(xiě)原始響應(yīng)中的某個(gè)Header Header名稱,值的正則表達(dá)式勾栗,重寫(xiě)后的值
SaveSession 在轉(zhuǎn)發(fā)請(qǐng)求之前惨篱,強(qiáng)制執(zhí)行WebSession::save操作 無(wú)
secureHeaders 為原始響應(yīng)添加一系列起安全作用的響應(yīng)頭 無(wú),支持修改這些安全響應(yīng)頭的值
SetPath 修改原始的請(qǐng)求路徑 修改后的路徑
SetResponseHeader 修改原始響應(yīng)中某個(gè)Header的值 Header名稱围俘,修改后的值
SetStatus 修改原始響應(yīng)的狀態(tài)碼 HTTP 狀態(tài)碼砸讳,可以是數(shù)字,也可以是字符串
StripPrefix 用于截?cái)嘣颊?qǐng)求的路徑 使用數(shù)字表示要截?cái)嗟穆窂降臄?shù)量
Retry 針對(duì)不同的響應(yīng)進(jìn)行重試 retries界牡、statuses簿寂、methods、series
RequestSize 設(shè)置允許接收最大請(qǐng)求包的大小宿亡。如果請(qǐng)求包大小超過(guò)設(shè)置的值陶耍,則返回 413 Payload Too Large 請(qǐng)求包大小,單位為字節(jié)她混,默認(rèn)值為5M
ModifyRequestBody 在轉(zhuǎn)發(fā)請(qǐng)求之前修改原始請(qǐng)求體內(nèi)容 修改后的請(qǐng)求體內(nèi)容
ModifyResponseBody 修改原始響應(yīng)體的內(nèi)容 修改后的響應(yīng)體內(nèi)容
9.6.1.1.2、局部過(guò)濾器的使用
server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù)
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務(wù),并遵循負(fù)載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個(gè)規(guī)定的才進(jìn)行1轉(zhuǎn)發(fā)
          filters:
            - StripPrefix=1 # 將第一層去掉
            - SetStatus=2000 # 這里使用內(nèi)置的過(guò)濾器泊碑,修改返回狀態(tài)
復(fù)制代碼

9.6.1.2坤按、自定義局部過(guò)濾器

很多的時(shí)候,內(nèi)置過(guò)濾器沒(méi)辦法滿足我們的需求馒过,這個(gè)時(shí)候就必須自定義局部過(guò)濾器臭脓。我們假定一個(gè)需求是:統(tǒng)計(jì)訂單服務(wù)調(diào)用耗時(shí)。

編寫(xiě)一個(gè)類腹忽,用于實(shí)現(xiàn)邏輯

**名稱是有固定格式xxxGatewayFilterFactory**
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {

  private static final String BEGIN_TIME = "beginTime";

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

  //讀取配置文件中的參數(shù) 賦值到 配置類中
  @Override
  public List<String> shortcutFieldOrder() {
    return Arrays.asList("show");
  }

  @Override
  public GatewayFilter apply(Config config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!config.show){
          // 如果配置類中的show為false来累,表示放行
          return chain.filter(exchange);
        }
        exchange.getAttributes().put(BEGIN_TIME, System.currentTimeMillis());
        /**
         *  pre的邏輯
         * chain.filter().then(Mono.fromRunable(()->{
         *     post的邏輯
         * }))
         */
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
          Long startTime = exchange.getAttribute(BEGIN_TIME);
          if (startTime != null) {
            System.out.println(exchange.getRequest().getURI() + "請(qǐng)求耗時(shí): " + (System.currentTimeMillis() - startTime) + "ms");
          }
        }));
      }
    };
  }

  @Setter
  @Getter
  static class Config{
    private boolean show;
  }

}
復(fù)制代碼

編寫(xiě)application.xml

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true # 讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù)
      routes:
        - id: product_route # 路由的名字
          uri: lb://product-service # lb指的是從nacos中按照名稱獲取微服務(wù),并遵循負(fù)載均衡策略
          predicates:
            - Path=/product-serv/** # 符合這個(gè)規(guī)定的才進(jìn)行1轉(zhuǎn)發(fā)
          filters:
            - StripPrefix=1 # 將第一層去掉
        - id: order_route
          uri: lb://order-service
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
            - Time=true
復(fù)制代碼

訪問(wèn)路徑:http://localhost:9000/order-serv/getById?o=1&pid=1

9.6.2、全局過(guò)濾器

全局過(guò)濾器作用于所有路由, 無(wú)需配置窘奏。通過(guò)全局過(guò)濾器可以實(shí)現(xiàn)對(duì)權(quán)限的統(tǒng)一校驗(yàn)嘹锁,安全性驗(yàn)證等功能。SpringCloud Gateway內(nèi)部也是通過(guò)一系列的內(nèi)置全局過(guò)濾器對(duì)整個(gè)路由轉(zhuǎn)發(fā)進(jìn)行處理着裹。
開(kāi)發(fā)中的鑒權(quán)邏輯:
  • 當(dāng)客戶端第一次請(qǐng)求服務(wù)時(shí)领猾,服務(wù)端對(duì)用戶進(jìn)行信息認(rèn)證(登錄)。
  • 認(rèn)證通過(guò),將用戶信息進(jìn)行加密形成token摔竿,返回給客戶端面粮,作為登錄憑證。
  • 以后每次請(qǐng)求继低,客戶端都攜帶認(rèn)證的token熬苍。
  • 服務(wù)端對(duì)token進(jìn)行解密,判斷是否有效袁翁。
我們來(lái)模擬一個(gè)需求:實(shí)現(xiàn)統(tǒng)一鑒權(quán)的功能,我們需要在網(wǎng)關(guān)判斷請(qǐng)求中是否包含token且柴底,如果沒(méi)有則不轉(zhuǎn)發(fā)路由,有則執(zhí)行正常邏輯梦裂。

編寫(xiě)全局過(guò)濾器

@Component
public class AuthGlobalFilter implements GlobalFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String token = exchange.getRequest().getQueryParams().getFirst("token");
    if (StringUtils.isBlank(token)) {
      System.out.println("鑒權(quán)失敗");
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }
    return chain.filter(exchange);
  }
}
復(fù)制代碼

9.6.3似枕、網(wǎng)關(guān)限流

網(wǎng)關(guān)是所有請(qǐng)求的公共入口,所以可以在網(wǎng)關(guān)進(jìn)行限流年柠,而且限流的方式也很多凿歼,我們本次采用前面學(xué)過(guò)的Sentinel組件來(lái)實(shí)現(xiàn)網(wǎng)關(guān)的限流。Sentinel支持對(duì)SpringCloud Gateway冗恨、Zuul等主流網(wǎng)關(guān)進(jìn)行限流答憔。
從1.6.0版本開(kāi)始,Sentinel提供了SpringCloud Gateway的適配模塊掀抹,可以提供兩種資源維度的限流:
  • route維度:即在Spring配置文件中配置的路由條目虐拓,資源名為對(duì)應(yīng)的routeId
  • 自定義API維度:用戶可以利用Sentinel提供的API來(lái)自定義一些API分組

9.6.3.1、網(wǎng)關(guān)集成Sentinel

添加依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    sentinel-spring-cloud-gateway-adapter
</dependency>
復(fù)制代碼

編寫(xiě)配置類進(jìn)行限流

配置類的本質(zhì)是用代碼替代nacos圖形化界面限流傲武。
@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 SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
    // 初始化一個(gè)限流的過(guò)濾器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
    //增加對(duì)商品微服務(wù)的限流
     @PostConstruct
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_route")
                .setCount(3) // 三次
                .setIntervalSec(1) // 一秒蓉驹,表示一秒鐘1超過(guò)了三次就會(huì)限流
        );
        GatewayRuleManager.loadRules(rules);
    }
}
復(fù)制代碼

修改限流默認(rèn)返回格式

如果我們不想在限流的時(shí)候返回默認(rèn)的錯(cuò)誤,那么就需要自定義錯(cuò)誤揪利,指定自定義的返回格式态兴。我們只需在類中添加一段配置即可。
@PostConstruct
public void initBlockHandlers() {
    BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
    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).
                body(BodyInserters.fromValue(map));
            }
};
    GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
復(fù)制代碼

測(cè)試

9.6.3.2疟位、自定義API分組

我們可以發(fā)現(xiàn)瞻润,上面的這種定義,對(duì)整個(gè)服務(wù)進(jìn)行了限流甜刻,粒度不夠細(xì)绍撞。自定義API分組是一種更細(xì)粒度的限流規(guī)則定義,它可以實(shí)現(xiàn)某個(gè)方法的細(xì)粒度限流得院。

在Shop-order-server項(xiàng)目中添加ApiController

@RestController
@RequestMapping("/api")
public class ApiController {
    @RequestMapping("/hello")
    public String api1(){
        return "api";
    }
}
復(fù)制代碼

在GatewayConfiguration中添加配置

@PostConstruct
private void initCustomizedApis() {
    Set<ApiDefinition> definitions = new HashSet<>();
    ApiDefinition api1 = new ApiDefinition("order_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/order-serv/api/**").                 setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
    definitions.add(api1);
    GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
@PostConstruct
private void initGatewayRules() {
    Set<GatewayFlowRule> rules = new HashSet<>();
    rules.add(new GatewayFlowRule("product_route")
                .setCount(3)
                .setIntervalSec(1)
    );
    rules.add(new GatewayFlowRule("order_api").
                setCount(1).
                setIntervalSec(1));
    GatewayRuleManager.loadRules(rules);
}
復(fù)制代碼

測(cè)試

直接訪問(wèn)[http://localhost:8082/api/hello](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8082%2Fapi%2Fhello "http://localhost:8082/api/hello") 是不會(huì)發(fā)生限流的傻铣,訪問(wèn)[http://localhost:9000/order-serv/api/hello](https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A9000%2Forder-serv%2Fapi%2Fhello "http://localhost:9000/order-serv/api/hello") 就會(huì)出現(xiàn)限流了。

作者:XiaoLin_Java
鏈接:https://juejin.cn/post/7001816849826447397
來(lái)源:稀土掘金
著作權(quán)歸作者所有祥绞。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)矾柜,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處阱驾。
微信公眾號(hào)【程序員黃小斜】作者是前螞蟻金服Java工程師,專注分享Java技術(shù)干貨和求職成長(zhǎng)心得怪蔑,不限于BAT面試里覆,算法、計(jì)算機(jī)基礎(chǔ)缆瓣、數(shù)據(jù)庫(kù)喧枷、分布式、spring全家桶弓坞、微服務(wù)隧甚、高并發(fā)、JVM渡冻、Docker容器戚扳,ELK、大數(shù)據(jù)等族吻。關(guān)注后回復(fù)【book】領(lǐng)取精選20本Java面試必備精品電子書(shū)帽借。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市超歌,隨后出現(xiàn)的幾起案子砍艾,更是在濱河造成了極大的恐慌,老刑警劉巖巍举,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脆荷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡懊悯,警方通過(guò)查閱死者的電腦和手機(jī)蜓谋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)炭分,“玉大人桃焕,你說(shuō)我怎么就攤上這事∏分希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵退子,是天一觀的道長(zhǎng)岖妄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)寂祥,這世上最難降的妖魔是什么荐虐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮丸凭,結(jié)果婚禮上福扬,老公的妹妹穿的比我還像新娘腕铸。我一直安慰自己,他們只是感情好铛碑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布狠裹。 她就那樣靜靜地躺著,像睡著了一般汽烦。 火紅的嫁衣襯著肌膚如雪涛菠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天撇吞,我揣著相機(jī)與錄音俗冻,去河邊找鬼。 笑死牍颈,一個(gè)胖子當(dāng)著我的面吹牛迄薄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播煮岁,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼讥蔽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了人乓?” 一聲冷哼從身側(cè)響起勤篮,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎色罚,沒(méi)想到半個(gè)月后碰缔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戳护,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年金抡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腌且。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梗肝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铺董,到底是詐尸還是另有隱情巫击,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布精续,位于F島的核電站坝锰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏重付。R本人自食惡果不足惜顷级,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望确垫。 院中可真熱鬧弓颈,春花似錦帽芽、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至橘蜜,卻和暖如春菊匿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背计福。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工跌捆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人象颖。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓佩厚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親说订。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抄瓦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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