Alibaba Sentinel流控详解

共 9741字,需浏览 20分钟

 ·

2021-06-13 18:17

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

概述

 流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

 

限流类型分为:

  • QPS 每秒请求数限制

  • 线程数 资源使用线程数限制

流控模式

  • 直接 资源直接限流,这个就是简单的限流。

  • 关联 关联模式需要填写关联资源的路径,意为如果关联资源的流量超额之后,限流自己(自己为资源名填写的路径)。

  • 链路 如果是链路模式需要填写入口资源,限制入口资源对自己的调用,这里不太好理解,下面进行测试。

流控效果

  • 快速失败 直接抛异常了

  • Warm Up 需要填写预热时间,表示,在预热时间内,慢慢的达到QPS的限制。

  • 排队等待 需要填写超时时间,排队很好理解啦。

1.QPS测试

设置/hello的QPS为1。

@GetMapping(value = "/hello")
public String hello() {
    return "Hello Sentinel1";
}

这个比较好理解,直接快速访问hello即可。

正常的时候返回

Hello Sentinel1

如果访问太快,会返回如下,表示流量控制。

Blocked by Sentinel (flow limiting)

2.线程数测试

线程数限制,表示执行当前方法的线程数控制,因为访问太快,加入sleep,方便测试。

@GetMapping(value = "/hello")
public String hello() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello Sentinel1";
}

线程数设置1

使用jmeter进行测试。jmeter设置一秒2个请求。在View Results Tree中会有1个成功1个限流。

至于QPS限制,还是线程限制,需要不同的场景,进行选择。

3.关联测试

设置/hello的关联资源为/hello2,表示如果hello2超过每秒1次的话,限制hello1。这个久很怪异,明明不管我的的事,却要限制我,但是存在即合理,应该也有这样的场景。

    @GetMapping(value = "/hello")
    public String hello() {
        return "Hello Sentinel1";
    }
    @GetMapping(value = "/hello2")
    public String hello2() {
        return "Hello Sentinel2";
    }

先通过jmeter对hello进行频繁访问,正常返回,没有测试出限流。再对hello2进行频繁访问,正常返回,没有限流。

验证关联控制。jmeter设置hello2为10秒请求30次,表示前10秒内hello返回限流。hello在这15秒请求15次,多5秒就是想测试没有了hello2的频繁访问,是否还限流。

设置信息如下。


hello2的返回信息一切正常。

hello前10秒限流,后5秒正常返回。

4.链路测试

    @Autowired
    private TestService testService;
    @GetMapping(value = "/hello")
    public String hello() {
        testService.listOrder();
        return "Hello Sentinel1";
    }
    @GetMapping(value = "/hello2")
    public String hello2() {
        testService.listOrder();
        return "Hello Sentinel2";
    }

@Service
public class TestService {
    //资源信息
    @SentinelResource("listOrder")
    public void listOrder(){
        System.out.println("listOrder");
    }
}

这样就形成了一个链路。一个controller对service调用的链路。在页面上有如下信息。

配置listOrder流控,链路入口为/hello

如此,配置即可限制/hellolistOrder的调用次数。当然在微服务之间也可以这么去设置。现在又对这个针对来源的概念模糊了,这两个不一样么?针对来源默认为default,,表示不区分来源,处理服务之间的限制,如果细化到方法的层面上,针对来源的设置无法做到,这时就需要进行链路设置。

5.快速失败

流控效果中快速失败是最简单的限流处理策略,直接抛了个异常。

自定义失败

在被限流的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉 BlockException 来自定义被限流之后的处理逻辑。

官方是这么说的,但是在直接对controller的接口进行限制的时候,通过spring全局异常捕获死活捕获不到。后来发现这里发现,这里进行controller的流量控制是通过Filter来控制的,所有spring的全局异常根本无法捕获。

@ControllerAdvice
@RestController
public class CommonException {
    @ExceptionHandler(BlockException.class)
    public String customException(Exception e) {
        return "系统出小差,请稍后再试。";
    }
    @ExceptionHandler(UndeclaredThrowableException.class)
    public String undeclaredThrowableException(Exception e) {
        return "系统出小差,请稍后再试。";
    }
    @ExceptionHandler(Exception.class)
    public String exception(Exception e) {
        return "系统出小差,请稍后再试。";
    }
}

如此,可以通过WebCallbackManager.setUrlBlockHandler设置限流之后的处理类。

注意:Filter的具体实现在CommonFilter中,有的版本通过拦截器实现,可以试着搜索AbstractSentinelInterceptor,可能这种小改动不会影响什么,所以貌似官方没有说明。

@Configuration
public class Config {
static {
    WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
        @Override
        public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
            // 简单演示,这里可以自行处理
            PrintWriter out = response.getWriter();
            out.print("try again later");
            out.flush();
            out.close();
        }
    });
}
}

当然,全局异常处理并不能解决所有的问题,更多的时候是希望每个方法都有自己的处理逻辑。

   @SentinelResource(value = "helloResource" ,blockHandler = "getOrderDowngradeRtTypeFallback",fallback = "helloFallback")
    @GetMapping(value = "/hello")
    public String hello(@RequestParam(value = "id") Long id)  {
        if(id==1l){
            throw  new RuntimeException("1231");
        }
        return "SUCCESS";
    }
    public String helloFallback(Long id,Throwable e) {
        System.out.println(1111);
        return id+"helloFallback";
    }
    public String getOrderDowngradeRtTypeFallback(Long id,BlockException ex) {
        return "服务降级啦,当前服务器请求次数过多,请稍后重试!";
    }

通过SentinelResource注解来自定义异常的处理。

  • blockHandler限流之后的补偿处理方法。这里是限流的请求才会执行这个方法。另外,还有blockHandlerClass效果一样。

  • fallback 如果方法中有异常则执行fallback指定的方法。另外,还有fallbackClass指定处理类,效果是一样的。

另外需要注意的是,如果添加SentinelResource注解之后,在控制台会有如下信息。两个都可以对这个方法进行限制,但是/hello是通过Filter实现的(后面改成了拦截器)全局异常可能是拦截不到的哦,具体可以看源码CommonFilter或者AbstractSentinelInterceptorhelloResource是通过AOP来实现的,所以全局异常是可以对此进行处理的,源码SentinelResourceAspect

在使用之前版本的时候fallback 死活不好用,不知道是不是版本BUG。换成了这个版本就好用了, 也是在这个版本使用的时候,发现conroller限流从Filter变成了拦截器。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

6.Warm Up

这个是一个预热的概念,比如服务刚刚启动,所有的缓存还在加载中,不能有太多的请求进来,所以需要进行一个服务的预热,刚启动QPS限制小于设定数值,待服务启动一段时间后,慢慢的达到所设置的QPS。

 规则设置之后开始预热。

如此设置,即可发现,开始的5秒中,QPS特别小,然后慢慢增加,到5秒的时候达到每秒10QPS。

7.排队等待

设置超时时间后,如果QPS限制之后,会等待,超过设定时间后,抛出BlockException异常。



本文链接:

https://blog.csdn.net/qq_30285985/article/details/107692608








浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报