距离上次总结Sentinel的滑动窗口算法已经有些时间了,原本想着一口气将它的core模块全部总结完,但是中间一懒就又松懈下来了,这几天在工作之余又重新整理了一下,在这里做一个学习总结。

上篇滑动窗口算法总结链接:https://www.cnblogs.com/mrxiaobai-wen/p/14212637.html

今天主要总结了一下Sentinel的快速失败和匀速排队的漏桶算法。因为它的WarmUpController和WarmUpRateLimiterController对应的令牌桶算法的数学计算原理有一点点复杂,所以我准备在后面单独用一篇来总结。所以今天涉及到的主要就是DefaultController和RateLimiterController。


限流策略入口

首先进入到FlowRuleUtil类中,方法generateRater就是对应策略的创建,逻辑比较简单,代码如下:

private static TrafficShapingController generateRater(FlowRule rule) {
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
switch (rule.getControlBehavior()) {
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
// WarmUp-令牌桶算法
return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(),
ColdFactorProperty.coldFactor);
case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
// 排队等待-漏桶算法
return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
// 预热和匀速排队结合
return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(),
rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);
case RuleConstant.CONTROL_BEHAVIOR_DEFAULT:
default:
// Default mode or unknown mode: default traffic shaping controller (fast-reject).
}
}
// 快速失败
return new DefaultController(rule.getCount(), rule.getGrade());
}

快速失败DefaultController

默认流控算法代码如下:

@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
int curCount = avgUsedTokens(node);
// 当前阈值 + acquireCount 是否大于规则设定的count,小于等于则表示符合阈值设定直接返回true
if (curCount + acquireCount > count) {
// 在大于的情况下,针对QPS的情况会对先进来的请求进行特殊处理
if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {
long currentTime;
long waitInMs;
currentTime = TimeUtil.currentTimeMillis();
// 如果策略是QPS,那么对于优先请求尝试去占用下一个时间窗口中的令牌
waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);
if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {
node.addWaitingRequest(currentTime + waitInMs, acquireCount);
node.addOccupiedPass(acquireCount);
sleep(waitInMs); // PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}.
throw new PriorityWaitException(waitInMs);
}
}
return false;
}
return true;
}

先看一下涉及到的avgUsedTokens方法:

private int avgUsedTokens(Node node) {
if (node == null) {
return DEFAULT_AVG_USED_TOKENS;
}
// 获取当前qps或者当前线程数
return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());
}

主要是获取已使用的令牌数,如果设置的阈值类型为线程数,那么返回当前统计节点中保存的线程数,如果设置的阈值类型为QPS,那么返回已经通过的QPS数。

然后回到上面的canPass方法,其主要逻辑就是在获取到目前节点的统计数据后,将已占用的令牌数与请求的令牌数相加,如果小于设定的阈值,那么直接放行。

如果大于设置的阈值,那么在阈值类型为QPS且允许优先处理先到的请求的情况下进行特殊处理,否则返回false不放行。

上面特殊处理就是:首先尝试去占用后面的时间窗口的令牌,获取到等待时间,如果等待时间小于设置的最长等待时长,那么就进行等待,当等待到指定时间后返回。否则直接返回false不放行。

由代码可以看出,在等待指定时长后,抛出PriorityWaitException进行放行,对应实现的地方在StatisticSlot中,对应entry方法代码如下:

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
try {
// Do some checking.
fireEntry(context, resourceWrapper, node, count, prioritized, args);
// 说明:省略了执行通过的处理逻辑
} catch (PriorityWaitException ex) {
node.increaseThreadNum();
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().increaseThreadNum();
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
Constants.ENTRY_NODE.increaseThreadNum();
}
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (BlockException e) {
// 说明:省略了阻塞异常处理逻辑
throw e;
} catch (Throwable e) {
context.getCurEntry().setError(e);
throw e;
}
}

对这个方法去除了其它多余代码,可以看出在PriorityWaitException异常捕捉的代码中没有继续抛出,所以对该请求进行了放行。


匀速排队-漏桶算法RateLimiterController

对于漏桶算法,首先在网上盗用一张图如下:

图片来源:https://blog.csdn.net/tianyaleixiaowu/article/details/74942405

其思路是:水流(请求)先进入到漏桶里,漏桶以一定的速率匀速流出,当流入量过大的时候,多余水流(请求)直接溢出,从而达到对系统容量的保护。

对应Sentinel使用漏桶算法进行流量整形的效果就如下图所示:

来看RateLimiterController的canPass方法:

@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
if (acquireCount <= 0) {
return true;
}
if (count <= 0) {
return false;
} long currentTime = TimeUtil.currentTimeMillis();
// 计算此次令牌颁发所需要的时间,其中: (1.0 / count * 1000)代表每个令牌生成的耗时,然后乘以acquireCount得到此次所需令牌生成耗时
long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
// 在上次通过时间的基础上加上本次的耗时,得到期望通过的时间点
long expectedTime = costTime + latestPassedTime.get(); if (expectedTime <= currentTime) {
// 如果期望时间小于当前时间,那么说明当前令牌充足,可以放行,同时将当前时间设置为上次通过时间
latestPassedTime.set(currentTime);
return true;
} else {
// 当期望时间大于当前时间,那么说明令牌不够,需要等待
long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();
if (waitTime > maxQueueingTimeMs) {
// 如果需要等待时间大于设置的最大等待时长,那么直接丢弃,不用等待,下面同理
return false;
} else {
long oldTime = latestPassedTime.addAndGet(costTime);
try {
// 再次检查等待时长
waitTime = oldTime - TimeUtil.currentTimeMillis();
if (waitTime > maxQueueingTimeMs) {
latestPassedTime.addAndGet(-costTime);
return false;
}
// in race condition waitTime may <= 0
if (waitTime > 0) {
Thread.sleep(waitTime);
}
return true;
} catch (InterruptedException e) {
}
}
}
return false;
}

Sentinel的令牌桶算法和漏桶算法都参考了Guava RateLimiter的设计。

上面的逻辑很清晰,其思路就是根据当前令牌请求数量acquireCount乘以令牌生成速率得到本次所需令牌的生成时间,然后加上上次通过时间得到一个本次请求的期望通过时间,如果期望通过时间小于当前时间那么说明容量足够直接通过,如果期望通过时间大于当前时间那么说明系统容量不够需要等待,然后结合设置的等待时间判断是继续等待还是直接放弃。

需要特别注意的是,匀速模式具有局限性,它只支持1000以内的QPS。我们可以看对应的语句:

long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
long expectedTime = costTime + latestPassedTime.get();

很容易得到如下结果,每种阈值对应的令牌生成时间(单位:毫秒):

count costTime
100 10
1000 1
2000 1
3000 0

所以当阈值count大于2000后,每个令牌生成的时间间隔计算为0,那么后面的判断就没有意义了。所以Sentinel的匀速器只支持QPS在1000以内的请求。

Sentinel限流之快速失败和漏桶算法的更多相关文章

  1. Sentinel限流实现原理

    Sentinel限流的神秘面纱: 之前我们学习过限流比较主流的三种算法:漏桶,令牌桶,滑动窗口.而Sentinel采用的是最后一种,滑动窗口来实现限流的. 通过对Sentinel基础Api的使用,我们 ...

  2. Spring Cloud Alibaba 之 Sentinel 限流规则和控制台实例

    这一节我们通过一个简单的实例,学习Sentinel的基本应用. 一.Sentinel 限流核心概念 在学习Sentinel的具体应用之前,我们先来了解一下Sentinel中两个核心的概念,资源和规则. ...

  3. coding++:高并发解决方案限流技术---漏桶算法限流--demo

    1.漏桶算法 漏桶作为计量工具(The Leaky Bucket Algorithm as a Meter)时,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolici ...

  4. coding++:RateLimiter 限流算法之漏桶算法、令牌桶算法--简介

    RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类 <dependency> <groupId>com.google.guava</g ...

  5. ASP.NET Core中使用漏桶算法限流

    漏桶算法是限流的四大主流算法之一,其应用场景各种资料中介绍的不多,一般都是说应用在网络流量控制中.这里举两个例子: 1.目前家庭上网都会限制一个固定的带宽,比如100M.200M等,一栋楼有很多的用户 ...

  6. 5-4 Sentinel 限流_流控与降级

    Sentinel 介绍 什么是Sentinel Sentinel也是Spring Cloud Alibaba的组件 Sentinel英文翻译"哨兵\门卫" 随着微服务的流行,服务和 ...

  7. 亚马逊 MWS 开发者指南 漏桶算法

    流量控制与令牌桶算法|James Pan's Blog  https://blog.jamespan.me/2015/10/19/traffic-shaping-with-token-bucket 服 ...

  8. php 基于redis使用令牌桶算法 计数器 漏桶算法 实现流量控制

    通常在高并发和大流量的情况下,一般限流是必须的.为了保证服务器正常的压力.那我们就聊一下几种限流的算法. 计数器计数器是一种最常用的一种方法,在一段时间间隔内,处理请求的数量固定的,超的就不做处理. ...

  9. Alibaba Sentinel 限流与熔断初探(技巧篇)

    目录 1.Sentinel 是什么 ?主要能解决什么问题? 2.限流与熔断的使用场景 3.Sentinel 源码结构 4.在 IntelliJ IDEA 中运行 Sentine Demo 温馨提示:源 ...

随机推荐

  1. Robot Framework+adb框架自动化测试Android设备案例⑸——L1层测试用例

    一.L1层测试用例 1.初始化.robot *** Settings *** Resource ../L2层关键字.robot *** Test Cases *** 切换EMMC模式 [Tags] A ...

  2. 二、java多线程编程核心技术之(笔记)——如何停止线程?

    1.异常法 public class MyThread extends Thread { @Override public void run() { super.run(); try { for (i ...

  3. Linux下基于.NET5开发CAX应用

    <<.NET5下的三维应用程序开发>>一文中介绍了如何在.NET5下使用AnyCAD开发应用程序.相比.NET4.x,.NET5一大进步便是可以跨平台,即可以在Linux.Ma ...

  4. Vue高仿网易云网页端源码

      音乐播放器虽然烂大街了,但是作为前端没自己撸一个一直是个遗憾, 而且偶然间发现 pc 端 web 版的网易云音乐做的实在是太简陋了, 社区仿 pc 客户端的网易云也不多见,为了弥补这个遗憾, 就用 ...

  5. PHP与Mysql之间的纠缠(超详细)

    目录 第一章 PHP操作mysql数据库 index.html代码 connect.php代码如下: list.php代码如下: 第二章 PHP 会话管理和控制 一.php 会话控制 之 PHP中的C ...

  6. Day5 - 01 函数及函数的调用概念

    函数就是最基本的一种代码抽象的方式.函数只需写一次,就可以多次调用.Python本身内置了很多有用的函数,可以直接调用. 调用函数    要调用一个函数,需要知道函数的名称和参数.可以通过help(x ...

  7. 使用VNC远程linux机器

    一,在Windows机器上下载vnc客户端 vnc viewer是一款vnc远程客户端,可以很方便的从Windows机器远程安装了图形界面和vnc服务器的linux机器. 从官网下载https://w ...

  8. linux之文本编辑器vi常用命令

    由于经常在linux下面文本操作,所以这里稍微系统的总结一下自己常用的vi命令 1.打开命令: vi+filename  (还有各种打开的姿势,只不过我比较顺手这个) 2.退出命令: :q   退出而 ...

  9. ASP .Net Core 中间件的使用(一):搭建静态文件服务器/访问指定文件

    前言 随着Asp .Net Core的升级迭代,很多开发者都逐渐倾向于.net core开发. .net core是一个跨平台的应用程序,可以在windows.Linux.macOS系统上进行开发和部 ...

  10. Spring Data JPA 的 Specifications动态查询

    主要的结构: 有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询. ...