Spring Cloud Alibaba Sentinel 流量控制

流量控制的主要参数

一条限流规则(即对象 FlowRule )主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

  • resource:资源名,即限流规则的作用对象

  • count: 限流阈值

  • grade: 限流阈值类型,QPS(RuleConstant.FLOW_GRADE_QPS值为1)或线程数(RuleConstant.FLOW_GRADE_THREAD值为0),默认为1

  • strategy: 根据调用关系选择策略。

    • RuleConstant.STRATEGY_DIRECT(值为0)用于直接流量控制;
    • RuleConstant.STRATEGY_RELATE(值为1)用于相关流控制(具有相关资源),需和 FlowRule.ref_identity 参数共同作用
    • RuleConstant.STRATEGY_CHAIN(值为2)用于链流控制(通过入口资源),需和 FlowRule.ref_identity 参数共同作用
  • controlBehavior:流量控制的手段。

    • 直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT值为0)方式。该方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
    • 冷启动(RuleConstant.CONTROL_BEHAVIOR_WARM_UP值为1)方式。该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。
    • 匀速器(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER值为2)方式。这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
    • 冷启动 + 匀速器(RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER值为3)
  • limitApp:根据调用方进行流量控制。同一个资源名可以配置多条规则,规则的生效顺序为:{some_origin_name} > other > default

    • default:表示不区分调用者,来自任何调用者的请求都将进行限流统计。如果这个资源名的调用总和超过了这条规则定义的阈值,则触发限流。
    • {some_origin_name}:表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。例如 NodeA 配置了一条针对调用者caller1的规则,那么当且仅当来自 caller1 对 NodeA 的请求才会触发流量控制。
    • other:表示针对除 {some_origin_name} 以外的其余调用方的流量进行流量控制。例如,资源NodeA配置了一条针对调用者 caller1 的限流规则,同时又配置了一条调用者为 other 的规则,那么任意来自非 caller1 对 NodeA 的调用,都不能超过 other 这条规则定义的阈值。

链路限流

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

     	          machine-root
                    /       \
                   /         \
             Entrance1     Entrance2
                /             \
               /               \
      DefaultNode(nodeA)   DefaultNode(nodeA)

上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 FlowRule.strategy 为 RuleConstant.CHAIN,同时设置 FlowRule.ref_identity 为 Entrance1 来表示只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而对来自 Entrance2 的调用漠不关心。调用链的入口是通过 API 方法 ContextUtil.enter(name) 定义的。

具有关系的资源流量控制

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

流控记录的API接口

curl http://localhost:8719/cnode?id=resourceName

结果示例:

idx id   thread  pass  blocked   success  total Rt   1m-pass   1m-block   1m-all   exeption
2   abc647 0     46     0           46     46   1       2763      0         2763     0
  • thread: 代表当前处理该资源的线程数;
  • pass: 代表一秒内到来到的请求;
  • blocked: 代表一秒内被流量控制的请求数量;
  • success: 代表一秒内成功处理完的请求;
  • total: 代表到一秒内到来的请求以及被阻止的请求总和;
  • RT: 代表一秒内该资源的平均响应时间;
  • 1m-pass: 则是一分钟内到来的请求;
  • 1m-block: 则是一分钟内被阻止的请求;
  • 1m-all: 则是一分钟内到来的请求和被阻止的请求的总和;
  • exception: 则是一秒内业务本身异常的总和。

调用方限流的API接口

curl http://localhost:8719/origin?id=nodeA

结果示例:

id: nodeA
idx origin  threadNum passedQps blockedQps totalQps aRt   1m-passed 1m-blocked 1m-total 
1   caller1 0         0         0          0        0     0         0          0
2   caller2 0         0         0          0        0     0         0          0

动态规则 SPI 配置


/**
 * Nacos 地址
 */
public static final String NACOS_ADDRS = "localhost:8848";

/**
 * 对应 Nacos 的命名空间 ID
 *
 */
public static final String NACOS_SENTINEL_NAMESPACE = "fd315d16-12ef-4d67-b8e2-8dfe2e6667b5";

/**
 * Nacos group id
 */
public static final String NACOS_SENTINEL_GROUPID = "Sentinel_Demo_Group";

/**
 * 限流规则 dataID
 * 获取到的值为规则列表
 */
public static final String NACOS_SENTINEL_RULES_DATAID = "Flow-Rule";


// Nacos 配置
Properties nacosPro = new Properties();
nacosPro.put(PropertyKeyConst.SERVER_ADDR,NACOS_ADDRS);
nacosPro.put(PropertyKeyConst.NAMESPACE,NACOS_SENTINEL_NAMESPACE);

ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(nacosPro, NACOS_SENTINEL_GROUPID, NACOS_SENTINEL_RULES_DATAID,
         source -> JsonUtil.toGenericBean(source, new TypeReference<List<FlowRule>>(){}));
 FlowRuleManager.register2Property(ds.getProperty());

限流返回处理

默认情况下,资源被限流之后会返回默认的提示信息 Blocked by Sentinel (flow limiting)。其默认的实现是 DefaultBlockExceptionHandler 。若要对其进行修改,有如下两种方式:

  1. 设置 blockPage,最后Sentinel会通过Response对象的sendRedirect方法进行跳转
    • 通过 spring.cloud.sentinel.blockPage 配置限流页面地址
  2. 自定义实现 BlockExceptionHandler 接口(可参照 DefaultBlockExceptionHandler 来实现),并使用@Bean、@Component 等注册到Spring 容器中

自定义 BlockExceptionHandler 示例

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        response.setStatus(429);
        response.setCharacterEncoding("UTF-8");

        PrintWriter out = response.getWriter();
        out.print("您已被限制访问!");
        out.flush();
        out.close();
    }
}

UrlCleaner 资源清洗接口

Sentinel Web Filter 会将每个到来的不同的 URL 都作为不同的资源处理,因此对于 REST 风格的 API,需要自行实现 UrlCleaner 接口清洗一下资源(比如将满足 /foo/:id 的 URL 都归到 /foo/* 资源下),然后将其注册至 WebCallbackManager 中。否则会导致资源数量过多,超出资源数量阈值(目前是 6000)时多出的资源的规则将 不会生效。

UrlCleaner 还可以来过滤掉不希望统计的 URL,只需要在 UrlCleaner 中将不希望统计的 URL 转换成空字符串(“”)即可。

资源清洗接口示例

定义 UrlCleaner 接口的实现类,并使用@Bean、@Component 等注册到Spring 容器中即可

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
import org.springframework.stereotype.Component;

@Component
public class MyUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String originUrl) {
        if (originUrl == null || originUrl.isEmpty()) {
            return originUrl;
        }

        // 比如将满足 /foo/{id} 的 URL 都归到 /foo/*
        if (originUrl.startsWith("/foo/")) {
            return "/foo/*";
        }
        // 不希望统计 *.ico 的资源文件,可以将其转换为 empty string (since 1.6.3)
        if (originUrl.endsWith(".ico")) {
            return "";
        }
        return originUrl;
    }
}

以上接口的配置对应的自动化配置类为:com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration ,其还对 RequestOriginParser 进行了配置(此类在后面来源访问控制中会讲到)。其源码比较简单,此处不做过多讲解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值