前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Sentinel对Spring MVC接口进行限流

使用Sentinel对Spring MVC接口进行限流

作者头像
码农小胖哥
发布2020-10-10 10:28:43
1.6K0
发布2020-10-10 10:28:43
举报

1.前言

Spring Cloud Alibaba提供了中间件Sentinel,它以流量为切入点,提供了流量控制、熔断降级、系统负载保护等多个功能来保护服务的稳定性。今天就来尝试一下。

本文是在 Spring Boot 2.3.4.RELEASE 的基础之上构建的

2.依赖引入

和其它教程通过Spring Cloud Starter引入的不同,这里使用更加底层一些的依赖引入来让我们深入的了解一些Sentinel,这里引入的是1.8.0版本。

代码语言:javascript
复制
<!--Sentinel 核心包-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--@SentinelResource注解AOP切面支持-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--针对Spring MVC的适配器 Spring Webflux 可引入对应的适配器-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-webmvc-adapter</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--负责同 dashboard 进行通信  如果你没有使用 sentinel dashboard 它是可选的-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>${sentinel.version}</version>
</dependency>

这里引入了SentinelSpring MVC的相关适配器和注解支持。

3. 使用

Sentinel的限流首先要制定限流规则,然后针对规则进行资源的标记。通过监控标记资源流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

流控规则

规则被封装到FlowRule对象中,需要声明的属性说明如下:

属性

说明

默认值

resource

资源名,资源名是限流规则的作用对象,不建议使用默认值

count

限流阈值,可以是 QPS 阈值,也可以是并发线程数阈值

grade

限流阈值类型,QPS 模式(1)或并发线程数模式(0)

QPS 模式

limitApp

可针对性的对特定客户端的请求进行流控

default,代表不区分调用来源

strategy

调用关系限流策略:直接、链路、关联

根据资源本身(直接)

controlBehavior

流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流

直接拒绝

clusterMode

是否集群限流

下面定义了一个规则并加载到内存中:

代码语言:javascript
复制
// 规则对应的类为FlowRule,用List保存,可以有多个规则
List<FlowRule> rules = new ArrayList<>();
FlowRule flowRule = new FlowRule();
// 设置资源名称
flowRule.setResource("bar");
// QPS为2
flowRule.setCount(2);
// 需要在限流过滤器中设置对应的解析策略来获取
// flowRule.setLimitApp(appName);
//限流的类型
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT);
rules.add(flowRule);
FlowRuleManager.loadRules(rules);

上面这种硬编码方式 一般仅用于测试和演示,生产上一般通过动态规则源的方式来动态管理规则。详细参考官方文档中关于动态流控规则的描述[1]

标记限流资源

传统情况下使用SphU 包含了 try-catch 风格的 API 进行限流操作。当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。基本范式如下:

代码语言:javascript
复制
// 1.5.0 版本开始可以利用 try-with-resources 特性(使用有限制)
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
  // 被保护的业务逻辑
  // do something here...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 在此处进行相应的处理操作
}

这种样板代码并不是非常优雅,所以Sentinel提供了@SentinelResource注解来简化开发,需要依赖sentinel-annotation-aspectj模块并显式的启用AOP切面类:

代码语言:javascript
复制
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
    return new SentinelResourceAspect();
}

然后我们可以在Spring MVC接口上进行如下标记即可:

代码语言:javascript
复制
@SentinelResource(value = "bar", entryType = EntryType.IN)
@GetMapping("/bar")
public String bar() {
    return "bar";
}

无论异常处理还是注解都只建议在学习中使用,实际生产中配合sentinel-dashboard可以更加方便地、集中地进行标记配置限流资源。详情参考控制台文档[2]

全局配置

我们还可以通过SentinelWebInterceptor或者SentinelWebTotalInterceptor来配置一些全局特性。

代码语言:javascript
复制
package cn.felord.sentinel.configuration;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class GlobalSentinelWebMvcConfiguration implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
        //指定 请求方法 POST  GET 等等
        sentinelWebMvcConfig.setHttpMethodSpecify(true);
        //默认使用统一Web上下文   如果希望支持链路关系的流控策略则应该设置为false
        sentinelWebMvcConfig.setWebContextUnify(true);
        // 统一的 BlockException 处理  FlowException(BlockException) 会被 JVM 的 UndeclaredThrowableException 包裹一层  某种原因并不能捕获到异常
//        sentinelWebMvcConfig.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
        // 用来标识来源 可针对性的对特定客户端的请求进行流控   limitApp
//        sentinelWebMvcConfig.setOriginParser(request -> request.getHeader("X-Client"));
 //       sentinelWebMvcConfig.setOriginParser(request -> request.getParameter("app"));

        //对原始的URL进行处理,比如去掉锚点之类的    /foo/bar?a=3#title  ->   /foo/bar?a=3
//        sentinelWebMvcConfig.setUrlCleaner( );
        registry.addInterceptor(new SentinelWebInterceptor(sentinelWebMvcConfig)).addPathPatterns("/**");
    }
}

流控规则中提到的limitApp就是通过setOriginParser 来配置获取策略的。

异常处理

虽然在全局配置中可以进行异常处理,但是经过胖哥测试提供的异常处理还是有些问题的,有时候无法捕捉到BlockException。我们可以通过控制器通知来切面捕获到它:

代码语言:javascript
复制
/**
 * 捕获BlockException异常.
 */
@Slf4j
@ControllerAdvice
@Order(0)
public class SentinelControllerAdvice {
    /**
     * 异常处理.
     *
     * @param request the request
     * @param e       the e
     * @return the object
     */
    @ExceptionHandler(BlockException.class)
    @ResponseBody
    public ResponseEntity<?> sentinelBlockHandler(HttpServletRequest request, BlockException e) {
        log.warn("Blocked by Sentinel: {}", e.getRule());
        // Return the customized result.
        HashMap<String, Object> map = new HashMap<>();
        map.put("path", request.getServletPath());
        map.put("msg", "limited by sentinel");
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(map);
    }
}

控制台

其实到这里限流功能已经可以使用了。不过配合控制台使用还是很简单的,先下载或者打包控制台的jar文件,通过下面的命令启动:

代码语言:javascript
复制
java  -Dserver.port=8088     -jar .\sentinel-dashboard-1.8.0.jar

客户端这时就可以移除注解相关的配置和样板代码了,但是拦截器GlobalSentinelWebMvcConfiguration配置务必保留。

客户端 ,例如sentinel-spring-boot.jar注册时,执行命令:

代码语言:javascript
复制
java -Dcsp.sentinel.dashboard.server=localhost:8088  -Dproject.name=sentinel-app -jar .\sentinel-spring-boot.jar

localhost:8088为服务器地址,sentinel-app为客户端名称。

注册完毕后,控制台依旧是白板。务必确保客户端有访问量Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包。你可以访问客户端的接口几次,然后刷新控制台,出现下面的

sentinel dashboard控制台

然后点击 +流控对接口GET:/foo/bar新增流控规则。以下是属性对照图:

FlowRule 属性对照

添加完毕针对GET:/foo/bar的限流就生效了。

控制台的实时流量监控数据只在内存保留 5 分钟,如果需要查历史流量甚至对接 Grafana 平台,就必须将监控数据持久化,网上有很多方案,有需要的可以自行搜索资料。

4.总结

今天对Sentinel的流控功能进行了简单的学习,其实它还可以实现很多有用的功能,熔断降级、热点参数限流、系统自适应限流、黑白名单控制等等。中文文档也算比较完备,有兴趣可以了解。好了今天就到这里,多多关注:码农小胖哥 获取更多的编程干货。

本文DEMO可通过关注公众号:码农小胖哥 回复 sentinel 获取。

参考资料

[1]

关于动态流控规则的描述: https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95

[2]

详情参考控制台文档: https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农小胖哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.前言
  • 2.依赖引入
  • 3. 使用
    • 流控规则
      • 标记限流资源
        • 全局配置
          • 异常处理
            • 控制台
            • 4.总结
              • 参考资料
              相关产品与服务
              消息队列 TDMQ
              消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档