1.常见限流算法
计数器算法
在指定周期内累加访问次数,当访问次数达到阈值时触发限流,当进入下一个时间周期时进行访问次数的清零。
问题:临界问题,相邻两个周期时间段内访问次数可能超出限制,但并未限流。
滑动窗口算法
在固定窗口中分割出多个小时间窗口,然后根据时间将窗口向前滑动并删除过期的小时间窗口,最终只统计滑动窗口范围内的所有小时间窗口总的计数。
令牌桶限流算法
固定大小的令牌桶(即限制最大请求量)
系统以恒定速度(r token/sec)往令牌桶中放入令牌
客户端请求执行前需要先获取并消耗令牌桶中的令牌
当请求速度大于令牌生成速度,令牌会被很快消耗,从而达到限流效果
问题:短时间内的突发流量无法处理
漏桶限流算法
内部维护一个容器
容器以恒定速度流出流量
当请求速度大于容器流出速度,则会触发限流
2.服务熔断
当服务无法提供正常服务时,为了防止雪崩效应,暂时将出现故障的接口隔离开来,后续一段时间调用的请求直接失败,直到目标服务恢复正常。
判断指标:平均响应时长,异常比例,异常数量
3.服务降级
将一些不重要的业务或接口的功能降低,可以只提供部分功能,也可以完全停掉所有不重要的功能。
4.Sentinel dashboard安装
启动命令
java -jar sentinel-dashboard.jar
-Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080
-Dcsp.sentinel.dashboard.server=localhost:8080 用于控制台对外暴露的服务地址
-Dproject.name=sentinel-dashboard 项目名称
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;
-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;
-Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
5.Sentinel实现流量限制
定义规则
List<FlowRule> rules = new ArrayList()<>;
FlowRule rule = new FlowRule();
// 资源名
rule.setResource(“resource1”);
// 限流阈值类型,QPS模式1,并发线程模式2
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 是否根据来源进行限流,默认未default,即不区分
rule.setLimitApp(“default”);
// 调用关系限流策略,直接,链路,关联
rule.setStrategy(RuleConstant.STRATEGY_CHAIN);
// 流控行为,直接拒绝,排队等待,慢启动模式,默认直接拒绝
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
// 是否为集群限流
rule.setClusterMode(false);
// 限流阈值
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
限流
try (Entry entry = SphU.entry(“resource1”)) {
// 业务逻辑
} catch (BlockException e) {
// 限流会抛出BlockException
}
注解形式
@SentinelResource(value = “resourceName”, blockHandler = “blockHandlerForResource1”)
Grade属性
FLOW_GRADE_THREAD(统计当前请求的上下文线程数量)
FLOW_GRADE_QPS(统计每秒查询数)
QPS流量控制行为
直接拒绝
Warm Up(冷启动(预热)):系统并不是直接将QPS限制拉到最大,而是逐步增加阈值
匀速排队:严格控制请求通过的间隔时间,也就是让请求以匀速的速度通过,类似于漏桶限流算法
调用关系流量策略
调用方限流:
根据请求来源进行流量控制,需要设置limitApp属性来设置来源信息
default:表示不区分调用者,也就是任何访问调用者的请求都会进行限流统计
{some_origin_name}:设置特定的调用者,只有来自这个调用者的请求才会进行流量统计和控制
other:表示针对除{some_origin_name}外的其他调用者进行流量控制根据调用链路入口限流:
关联流量控制:两个资源之间存在关联时,只限制其中一个资源
6.Sentinel实现服务熔断
List<DegradeRule> rules = new ArrayList()<>;
DegradeRule rule = new DegradeRule();
// 资源名
rule.setResource(“resource1”);
// 熔断策略
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
// 限流阈值
rule.setCount(10);
// 触发熔断降级后多长时间内自动熔断,单位s
rule.setTimeWindow(10);
// 触发熔断的最小请求数,请求数小于该值时即使异常比例超过阈值也不会触发熔断,默认5
rule.setMinRequestAmount(5);
// Grade为RT时,1s内持续多少个请求平均RT超出阈值触发熔断,默认5
rule.setRtSlowRequestAmount(5);
rules.add(rule);
三种熔断策略:
平均响应时间(RuleConstant.DEGRADE_GRADE_RT)
如果1s内持续进入5个请求,对应的平均响应时间都超过了阈值(count,单位ms)那么接下来的时间窗口(timeWindow,单位s)内,进行自动熔断,抛出DegradeException异常比例(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
如果每秒资源数 >= minRequestAmount(默认5),并且每秒异常占比超过阈值count(count的取值范围[0.0,1.0],代表0%到100%),则进行熔断…异常数(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
当资源最近一分钟的异常数目超过阈值后,触发熔断
7.热点数据限流
List<ParamFlowRule> rules = new ArrayList()<>;
ParamFlowRule rule = new ParamFlowRule(“resource1”);
// 热点参数的索引
rule.setParamIdx(0);
// 限流阈值类型,QPS模式1,并发线程模式2
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 限流阈值
rule.setCount(20);
rules.add(rule);
ParamRuleManager.loadRules(rules);
8.使用
客户端引入依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
2.1.1.RELEASE
yml配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:9100
配置规则
阈值类型/单机阈值:
QPS:每秒的请求量达到阈值,进行限流
线程数:当调用该api的线程数达到阈值时进行限流
流控模式:
直接:api达到限流条件时,直接限流
关联:当关联的资源达到阈值时,就限流自己
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流,但其它入口进来的不会限流)
熔断策略:
RT:平均响应时间(秒级统计)超出阈值 且 在时间窗口内的请求 >= 5时,触发降级(断路器打开);时间窗口结束后,关闭降级【Sentinel默认最大的RT为4900ms,可以通过-Dcsp.sentinel.statistic.max.rt=xxx修改】
异常比例:QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数:异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级【时间窗口 < 60秒可能会出现问题】
关于异常数这种降级策略需要注意的点:
若将时间窗口的值设置小于60秒则可能会出问题,因为异常数的统计是分钟级别的,时间窗口小于60秒就有可能不断进入降级状态.
spring cloud sentinel 处理的场景是 feign 远程调用的降级异常 tracer 记录了。
普通异常需要手动加Tracer.trace(e);
需要自己添加资源@SentinelResource(value = “resourceName”)
参数例外项:指定参数值进行额外限流
LOAD:(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps minRt 计算得出。设定参考值一般是 CPU cores 2.5
RT:当单台机器上所有入口流量的平均 RT(响应时间) 达到阈值即触发系统保护,单位是毫秒
流控应用(limitApp)指的时调用方的服务名
自定义限流异常
@Component
@Slf4j
public class MyBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
if (e instanceof FlowException) {
log.error("限流异常");
} else if (e instanceof DegradeException) {
log.error("降级异常");
} else if (e instanceof ParamFlowException) {
log.error("热点参数异常");
} else if (e instanceof SystemBlockException) {
log.error("系统异常");
} else if (e instanceof AuthorityException) {
log.error("授权异常");
}
httpServletResponse.setStatus(HttpStatus.OK.value());
R r = R.fail(ErrorCode.SENTINEL.getCode(), e.getMessage());
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
httpServletResponse.getWriter().print(JSON.toJSONString(r));
httpServletResponse.getWriter().flush();
}
}