Loading

03_Sentinel控制台规则配置

Sentinel控制台介绍

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。
Sentinel 控制台包含如下功能:
查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
规则管理和推送:统一管理推送规则。
鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
阿里云提供了 企业级的 Sentinel 控制台,应用高可用服务 AHAS。

实时监控

监控接口的通过的QPS和拒绝的QPS 。同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。

注意:请确保 Sentinel 控制台所在的机器时间与自己应用的机器时间保持一致,否则会导致拉不到实时的监控数据。

簇点链路

用来显示微服务的所监控的API。簇点链路(单机调用链路)页面实时的去拉取指定客户端资源的运行情况。
它一共提供两种展示模式:一种用树状结构展示资源的调用链路,另外一种则不区分调用链路展示资源的运行情况。
注意: 簇点监控是内存态的信息,它仅展示启动后调用过的资源。

流控规则

流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果。 

限流阈值类型

流量控制主要有两种统计类型:
一种是统计并发线程数,另外一种则是统计 QPS。
类型由 FlowRule 的 grade 字段来定义。其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制。
QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。 
进入簇点链路选择具体的访问的API,然后点击流控按钮:

测试:http://localhost:8800/user/findOrderByUserId/1

BlockException异常统一处理

springwebmvc接口资源限流入口在HandlerInterceptor的实现类AbstractSentinelInterceptor的preHandle方法中,对异常的处理是BlockExceptionHandler的实现类。
sentinel 1.7.1 引入了sentinel-spring-webmvc-adapter.jar自定义BlockExceptionHandler 的实现类统一处理BlockException 
@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        log.info("BlockExceptionHandler BlockException================" + e.getRule());
        R r = null;

        if (e instanceof FlowException) {
            r = R.error(100, "接口限流了");

        } else if (e instanceof DegradeException) {
            r = R.error(101, "服务降级了");

        } else if (e instanceof ParamFlowException) {
            r = R.error(102, "热点参数限流了");

        } else if (e instanceof SystemBlockException) {
            r = R.error(103, "触发系统保护规则了");

        } else if (e instanceof AuthorityException) {
            r = R.error(104, "授权规则不通过");
        }

        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), r);
    }
}

并发线程数

并发线程数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。
为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
线程休眠900ms让线程结束时间变长:
 @RequestMapping(value = "/findOrderByUserId/{id}")
    public R findOrderByUserId(@PathVariable("id") Integer id) {

        try {
            // 模拟测试并发线程数限流
            Thread.sleep(900);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //feign调用
        return orderFeignService.findOrderByUserId(id);
    }

配置并发线程数为5之后开始进行限流:

使用测试工具jmeter进行测试,结果如下:

流控模式

基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调用链路的层次关系。 

直接

以上的流控模式均为直接,很好理解,根据指定的阈值类型达到指定的阈值后,便进行流量控制。

关联

当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置流控模式: strategy 为关联模式: RuleConstant.STRATEGY_RELATE 同时设置关联资源 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。 

 测试:

使用测试工具jmeter对资源/user/findOrderByUserId/{id}进行循环请求状态正常,使用浏览器访问关联资源/user/info/{id}当QPS达到2后,便会触发限流。

链路

根据调用链路入口限流。
NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示: 

上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。 

测试会发现链路规则不生效,没有限流效果。
原因:
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,导致链路限流不生效,controller里的方法都会默认进去sentinel默认的根链路里,这样就只有一条链路,无法流控链路模式如下图。
从1.7.0版本开始,官方在CommonFilter引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context,将其配置为false即可根据不同的URL进行链路限流。 

注意:高版本此功能直接使用不生效,如何解决? 

1.8.4版本中 需要在yml中配置spring.cloud.sentinel.web-context-unify属性为false 
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        # 添加sentinel的控制台地址
        dashboard: 127.0.0.1:8080
        # 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
        port: 8719
#           将其配置为 false 即可根据不同的 URL 进行链路限流
      web-context-unify: false
再次测试链路规则,链路规则生效,但是页面出现异常:

原因分析:
1. Sentinel流控规则的处理核心是 FlowSlot, 对getUser资源进行了限流保护,当请求QPS超过阈值2的时候,就会触发流控规则抛出FlowException异常
2. 对getUser资源保护的方式是@SentinelResource注解模式,会在对应的SentinelResourceAspect切面逻辑中处理BlockException类型的FlowException异常
解决方案: 在@SentinelResource注解中指定blockHandler处理BlockException
如果此过程没有处理FlowException,AOP就会对异常进行处理,核心代码在CglibAopProxy.CglibMethodInvocation#proceed中,抛出UndeclaredThrowableException异常,此异常属于RuntimeException,所以不会被BlockException异常机制处理。 

流控效果

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:
快速失败(直接拒绝):达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
Warm Up(预热模式):对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
匀速排队(排队等待):让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长。
对应 FlowRule 中的 controlBehavior 字段。

快速失败

(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
测试用例:
QPS阈值设为15,预热时长为10s。则限流阈值会在10s内,逐渐从5升高至15。

 Jmeter参数设置300个线程20s内执行完毕即每秒15个线程请求资源/test4,则被拒绝的请求会在十秒内逐渐减少,10s后则请求会全部通过。

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

匀速排队

匀速排队(`RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER`)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
该方式的作用如下图所示: 

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
配置每200ms允许一个请求:

使用jemeter每秒请求6次,则每秒会有1个请求会进入等待,超时1s则限流快速失败。流量形状如图:

熔断降级规则

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

熔断降级规则说明

熔断降级规则(DegradeRule)包含下面几个重要的属性: 

熔断策略之慢调用比例

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
    @RequestMapping("/test/{id}")
    public String test(@PathVariable(name = "id") Integer id) {
        if (id == 2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return String.format("========test(%s)========", id);
    }

尝试方位/test/1几次后,访问/test/2慢调用,当累计超过8次并达到阈值比例后则会进入3S的熔断状态,之后访问/test/1则正常,访问/test/2则熔断器继续保持开启。

请求数达到8次以上且满调用比例大于30%后服务开始熔断降级:

熔断策略之异常比例

异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

达到指定阈值后服务开始降级:

熔断策略之异常数

异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

当异常数达到指定异常数后开始熔断降级:

热点规则

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。 

注意:
1. 热点规则需要使用@SentinelResource("resourceName")注解,否则不生效。
2. 参数必须是7种基本数据类型才会生效。

测试用例:

    @RequestMapping("/info/{id}")
    @SentinelResource(value = "userinfo", blockHandler = "handleException")
    public R info(@PathVariable("id") Integer id) {
        UserEntity user = userService.getById(id);
        return R.ok().put("user", user);
    }

配置热点参数规则

注意: 资源名必须是@SentinelResource(value="资源名")中 配置的资源名,热点规则依赖于注解 。

测试:http://localhost:8800/user/info/6的QPS阈值为1,http://localhost:8800/user/info/3的QPS阈值为2。

系统规则——系统自适应保护

Sentinel 做系统自适应保护的目的:
  1、保证系统不被拖垮。
  2、在系统稳定的前提下,保持系统的吞吐量。
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则阈值类型

Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores* 2.5。
CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。 
测试:

 当达到指定QPS阈值后,触发系统保护:

授权控制规则——来源访问控制(黑白名单)

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
来源访问控制规则(AuthorityRule)非常简单,主要有以下配置项:
resource:资源名,即限流规则的作用对象。
limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB。
strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式。
配置授权规则

实现com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser接口,在parseOrigin方法中区分来源,并交给spring管理。
注意:如果引入CommonFilter,此处会多出一个相同名称的接口 。
@Component
public class MyRequestOriginParser implements RequestOriginParser {

    /**
     * 通过request获取来源标识,交给授权规则进行匹配
     */
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 标识字段名称可以自定义   serviceName = order
        String origin = request.getParameter("serviceName");
        if (StringUtil.isBlank(origin)){
            throw new IllegalArgumentException("serviceName参数未指定");
        }
        return origin;
    }
}

请求测试:http://localhost:8800/user/findOrderByUserId/1?serviceName=order

集群规则

为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
Sentinel 集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:
集群总体模式:即限制整个集群内的某个资源的总体 qps 不超过此阈值。
单机均摊模式:单机均摊模式下配置的阈值等同于单机能够承受的限额,token server 会根据连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30),按照计算出的总的阈值来进行限制。这种方式根据当前的连接数实时计算总的阈值,对于机器经常进行变更的环境非常适合。

启动方式

Sentinel 集群限流服务端有两种启动方式:
独立模式(Alone),即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。

嵌入模式(Embedded),即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。 

云上版本 AHAS Sentinel 提供开箱即用的全自动托管集群流控能力,无需手动指定/分配 token server 以及管理连接状态,同时支持分钟小时级别流控、大流量低延时场景流控场景,同时支持 Istio/Envoy 场景的 Mesh 流控能力。

 

posted @ 2023-05-05 23:09  1640808365  阅读(242)  评论(0编辑  收藏  举报