欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

阿里Sentinel支持Spring Cloud Gateway的实现

程序员文章站 2023-12-10 17:49:04
1. 前言 4月25号,sentinel 1.6.0 正式发布,带来 spring cloud gateway 支持、控制台登录功能、改进的热点限流和注解 fallbac...

1. 前言

4月25号,sentinel 1.6.0 正式发布,带来 spring cloud gateway 支持、控制台登录功能、改进的热点限流和注解 fallback 等多项新特性,该出手时就出手,紧跟时代潮流,昨天刚发布,今天我就要给大家分享下如何使用!

2. 介绍(本段来自sentinel文档)

sentinel 1.6.0 引入了 sentinel api gateway adapter common 模块,此模块中包含网关限流的规则和自定义 api 的实体和管理逻辑:

gatewayflowrule:网关限流规则,针对 api gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 api 分组进行限流,支持针对请求中的参数、header、来源 ip 等进行定制化的限流。

apidefinition:用户自定义的 api 定义分组,可以看做是一些 url 匹配的组合。比如我们可以定义一个 api 叫 my_api,请求 path 模式为 /foo/ 和 /baz/ 的都归到 my_api 这个 api 分组下面。限流的时候可以针对这个自定义的 api 分组维度进行限流。

其中网关限流规则 gatewayflowrule 的字段解释如下:

  • resource:资源名称,可以是网关中的 route 名称或者用户自定义的 api 分组名称。
  • resourcemode:规则是针对 api gateway 的 route(resource_mode_route_id)还是用户在 sentinel 中定义的 api 分组(resource_mode_custom_api_name),默认是 route。
  • grade:限流指标维度,同限流规则的 grade 字段
  • count:限流阈值
  • intervalsec:统计时间窗口,单位是秒,默认是 1 秒
  • controlbehavior:流量整形的控制效果,同限流规则的 controlbehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。
  • burst:应对突发请求时额外允许的请求数目。
  • maxqueueingtimeoutms:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
  • paramitem:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:
  • parsestrategy:从请求中提取参数的策略,目前支持提取来源 ip(param_parse_strategy_client_ip)、host(param_parse_strategy_host)、任意 header(param_parse_strategy_header)和任意 url 参数(param_parse_strategy_url_param)四种模式。
  • fieldname:若提取策略选择 header 模式或 url 参数模式,则需要指定对应的 header 名称或 url 参数名称。
  • pattern 和 matchstrategy:为后续参数匹配特性预留,目前未实现。

用户可以通过 gatewayrulemanager.loadrules(rules) 手动加载网关规则,或通过 gatewayrulemanager.register2property(property) 注册动态规则源动态推送(推荐方式)。

3. 使用

3.1 快速体验

首先你的有一个spring cloud gateway的项目,如果没有,新建一个,增加gateway和sentinel-spring-cloud-gateway-adapter的依赖,如下:

<dependency>
  <groupid>org.springframework.cloud</groupid>
  <artifactid>spring-cloud-starter-gateway</artifactid>
</dependency>
<dependency>
  <groupid>com.alibaba.csp</groupid>
  <artifactid>sentinel-spring-cloud-gateway-adapter</artifactid>
  <version>1.6.0</version>
</dependency>

新建一个application.yml配置文件,用来配置路由:

server:
 port: 2001
spring:
 application:
  name: spring-cloud-gateway
 cloud:
  gateway:
   routes:
   - id: path_route
    uri: http://cxytiandi.com
    predicates:
    - path=/course

配置了path路由,等会使用 http://localhost:2001/course 进行访问即可。

增加一个gatewayconfiguration 类,用于配置gateway限流要用到的类,目前是手动配置的方式,后面肯定是可以通过注解启用,配置文件中指定限流规则的方式来使用,当然这部分工作会交给spring cloud alibaba来做,后面肯定会发新版本的,大家耐心等待就行了。

@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;
  }

 
  /**
   * 配置sentinelgatewayblockexceptionhandler,限流后异常处理
   * @return
   */
  @bean
  @order(ordered.highest_precedence)
  public sentinelgatewayblockexceptionhandler sentinelgatewayblockexceptionhandler() {
    return new sentinelgatewayblockexceptionhandler(viewresolvers, servercodecconfigurer);
  }

  /**
   * 配置sentinelgatewayfilter
   * @return
   */
  @bean
  @order(-1)
  public globalfilter sentinelgatewayfilter() {
    return new sentinelgatewayfilter();
  }
  
  @postconstruct
  public void doinit() {
    initgatewayrules();
  }

  /**
   * 配置限流规则
   */
  private void initgatewayrules() {
    set<gatewayflowrule> rules = new hashset<>();
    rules.add(new gatewayflowrule("path_route")
      .setcount(1) // 限流阈值
      .setintervalsec(1) // 统计时间窗口,单位是秒,默认是 1 秒
    );
    gatewayrulemanager.loadrules(rules);
  }
}

我们定义的资源名称是path_route,也就是application.yml中的路由id,一致就行。

在一秒钟内多次访问http://localhost:2001/course就可以看到限流启作用了。

阿里Sentinel支持Spring Cloud Gateway的实现

3.2 指定参数限流

上面的配置是针对整个路由来限流的,如果我们只想对某个路由的参数做限流,那么可以使用参数限流方式:

 rules.add(new gatewayflowrule("path_route")
   .setcount(1)
   .setintervalsec(1)
   .setparamitem(new gatewayparamflowitem()
        .setparsestrategy(sentinelgatewayconstants.param_parse_strategy_url_param).setfieldname("viptype")
   )
 );

通过指定param_parse_strategy_url_param表示从url中获取参数,setfieldname指定参数名称

3.3 自定义api分组

假设我有下面两个路由,我想让这两个路由共用一个限流规则,那么我们可以自定义进行组合:

 - id: path2_route
  uri: http://cxytiandi.com
  predicates:
  - path=/article
- id: path3_route
 uri: http://cxytiandi.com
 predicates:
 - path=/blog/**

自定义分组代码:

private void initcustomizedapis() {
  set<apidefinition> definitions = new hashset<>();
  apidefinition api1 = new apidefinition("customized_api")
    .setpredicateitems(new hashset<apipredicateitem>() {{
     // article完全匹配
     add(new apipathpredicateitem().setpattern("/article"));
     // blog/开头的
     add(new apipathpredicateitem().setpattern("/blog/**")
        .setmatchstrategy(sentinelgatewayconstants.param_match_strategy_prefix));
    }});
  definitions.add(api1);
  gatewayapidefinitionmanager.loadapidefinitions(definitions);
}

然后我们需要给customized_api这个资源进行配置:

rules.add(new gatewayflowrule("customized_api")
   .setcount(1)
   .setintervalsec(1)
 ); 

3.4 自定义异常提示

前面我们有看到,当触发限流后页面显示的是blocked by sentinel: flowexception,正常情况下,就算给出提示也要跟后端服务的数据格式一样,如果你后端都是json格式的数据,那么异常的提示也要是json的格式,所以问题来了,我们怎么去自定义异常的输出?

前面我们有配置sentinelgatewayblockexceptionhandler,我的注释写的限流后异常处理,我们可以进去看下源码就知道是不是异常处理了。下面贴出核心的代码:

 private mono<void> writeresponse(serverresponse response, serverwebexchange exchange) {
   return response.writeto(exchange, contextsupplier.get());
 }

 @override
 public mono<void> handle(serverwebexchange exchange, throwable ex) {
   if (exchange.getresponse().iscommitted()) {
     return mono.error(ex);
   }
   // this exception handler only handles rejection by sentinel.
   if (!blockexception.isblockexception(ex)) {
     return mono.error(ex);
   }
   return handleblockedrequest(exchange, ex)
     .flatmap(response -> writeresponse(response, exchange));
 }

重点在于writeresponse这个方法,我们只要把这个方法改掉,返回自己想要返回的数据就行了,可以自定义一个sentinelgatewayblockexceptionhandler的类来实现。

比如:

public class jsonsentinelgatewayblockexceptionhandler implements webexceptionhandler {
 // ........
}

然后复制sentinelgatewayblockexceptionhandler中的代码到jsonsentinelgatewayblockexceptionhandler 中,只改动writeresponse一个方法即可。

private mono<void> writeresponse(serverresponse response, serverwebexchange exchange) {
  serverhttpresponse serverhttpresponse = exchange.getresponse();
  serverhttpresponse.getheaders().add("content-type", "application/json;charset=utf-8");
  byte[] datas = "{\"code\":403,\"msg\":\"限流了\"}".getbytes(standardcharsets.utf_8);
  databuffer buffer = serverhttpresponse.bufferfactory().wrap(datas);
  return serverhttpresponse.writewith(mono.just(buffer));
}

最后将配置的sentinelgatewayblockexceptionhandler改成jsonsentinelgatewayblockexceptionhandler 。

 @bean
 @order(ordered.highest_precedence)
 public jsonsentinelgatewayblockexceptionhandler sentinelgatewayblockexceptionhandler() {
   return new jsonsentinelgatewayblockexceptionhandler(viewresolvers, servercodecconfigurer);
 }

阿里Sentinel支持Spring Cloud Gateway的实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。