Sentinel 和常见限流算法(下)

简介: 本文主要讲述常见的几种限流算法:计数器算法、漏桶算法、令牌桶算法。然后结合我对 Sentinel 1.8.0 的理解,给大家分享 Sentinel 在源码中如何使用这些算法进行流控判断。

漏桶算法


漏桶算法(Leaky Bucket)是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量, 执行过程如下图所示。


image.png


实现代码案例:


public class LeakyBucket {
  public long timeStamp = System.currentTimeMillis();  // 当前时间
  public long capacity; // 桶的容量
  public long rate; // 水漏出的速度
  public long water; // 当前水量(当前累积请求数)
  public boolean grant() {
    long now = System.currentTimeMillis();
    // 先执行漏水,计算剩余水量
    water = Math.max(0, water - (now - timeStamp) * rate); 
    timeStamp = now;
    if ((water + 1) < capacity) {
      // 尝试加水,并且水还未满
      water += 1;
      return true;
    } else {
      // 水满,拒绝加水
      return false;
    }
  }
}


说明:


(1)未满加水:通过代码 water +=1进行不停加水的动作。 (2)漏水:通过时间差来计算漏水量。 (3)剩余水量:总水量-漏水量。


在 Sentine 中RateLimiterController 实现了了漏桶算法 , 核心代码如下


@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
  // Pass when acquire count is less or equal than 0.
  if (acquireCount <= 0) {
    return true;
  }
  // Reject when count is less or equal than 0.
  // Otherwise,the costTime will be max of long and waitTime will overflow in some cases.
  if (count <= 0) {
    return false;
  }
  long currentTime = TimeUtil.currentTimeMillis();
  // Calculate the interval between every two requests.
  // 计算时间间隔
  long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
  // Expected pass time of this request.
  // 期望的执行时间
  long expectedTime = costTime + latestPassedTime.get();
  // 当前时间 > 期望时间
  if (expectedTime <= currentTime) {
    // Contention may exist here, but it's okay.
    // 可以通过,并且设置最后通过时间
    latestPassedTime.set(currentTime);
    return true;
  } else {
    // Calculate the time to wait.
    // 等待时间 = 期望时间 - 最后时间 - 当前时间
    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;
}


令牌桶算法


令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。如下图所示:


image.png


简单的说就是,一边请求时会消耗桶内的令牌,另一边会以固定速率往桶内放令牌。当消耗的请求大于放入的速率时,进行相应的措施,比如等待,或者拒绝等。


实现代码案例:


public class TokenBucket {
  public long timeStamp = System.currentTimeMillis();  // 当前时间
  public long capacity; // 桶的容量
  public long rate; // 令牌放入速度
  public long tokens; // 当前令牌数量
  public boolean grant() {
    long now = System.currentTimeMillis();
    // 先添加令牌
    tokens = Math.min(capacity, tokens + (now - timeStamp) * rate);
    timeStamp = now;
    if (tokens < 1) {
      // 若不到1个令牌,则拒绝
      return false;
    } else {
      // 还有令牌,领取令牌
      tokens -= 1;
      return true;
    }
  }
}


Sentinel 在 WarmUpController 中运用到了令牌桶算法,在这里可以实现对系统的预热,设定预热时间和水位线,对于预热期间多余的请求直接拒绝掉。


public boolean canPass(Node node, int acquireCount, boolean prioritized) {
  long passQps = (long) node.passQps();
  long previousQps = (long) node.previousPassQps();
  syncToken(previousQps);
  // 开始计算它的斜率
  // 如果进入了警戒线,开始调整他的qps
  long restToken = storedTokens.get();
  if (restToken >= warningToken) {
    long aboveToken = restToken - warningToken;
    // 消耗的速度要比warning快,但是要比慢
    // current interval = restToken*slope+1/count
    double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));
    if (passQps + acquireCount <= warningQps) {
      return true;
    }
  } else {
    if (passQps + acquireCount <= count) {
      return true;
    }
  }
  return false;
}


限流算法总结


计数器 VS 时间窗


  1. 时间窗算法的本质也是通过计数器算法实现的。


  1. 时间窗算法格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确,但是也会占用更多的内存存储。


漏桶 VS 令牌桶


  1. 漏桶算法和令牌桶算法本质上是为了做流量整形或速率限制,避免系统因为大流量而被打崩,但是两者的核心差异在于限流的方向是相反的


  1. 漏桶:限制的是流量的流出速率,是相对固定的。


  1. 令牌桶 : 限制的是流量的平均流入速率,并且允许一定程度的突然性流量,最大速率为桶的容量和生成token的速率。


  1. 在某些场景中,漏桶算法并不能有效的使用网络资源,因为漏桶的漏出速率是相对固定的,所以在网络情况比较好并且没有拥塞的状态下,漏桶依然是会有限制的,并不能放开量,因此并不能有效的利用网络资源。而令牌桶算法则不同,其在限制平均速率的同时,支持一定程度的突发流量。


参考文档


www.cnblogs.com/linjiqin/p/…

www.cnblogs.com/dijia478/p/…


相关文章
|
6天前
|
缓存 算法 Java
限流算法 - 基本实现
限流算法 - 基本实现
27 0
|
6天前
|
数据采集 算法 双11
高并发的场景下,不能不说的限流算法
高并发的场景下,不能不说的限流算法
29 1
|
6天前
|
Java 数据安全/隐私保护 Sentinel
面试官:Sentinel是如何实现限流的?
面试官:Sentinel是如何实现限流的?
441 1
|
6天前
|
编解码 算法 定位技术
GEE时序——利用sentinel-2(哨兵-2)数据进行地表物候学分析(时间序列平滑法估算和非平滑算法代码)
GEE时序——利用sentinel-2(哨兵-2)数据进行地表物候学分析(时间序列平滑法估算和非平滑算法代码)
107 3
|
6天前
|
存储 算法 NoSQL
|
6天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
6天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba Sentinel实现熔断与限流--学习笔记
SpringCloud Alibaba Sentinel实现熔断与限流--学习笔记
30 0
|
6天前
|
算法 NoSQL JavaScript
常见的限流算法-python版本
常见的限流算法-python版本
22 0
常见的限流算法-python版本
|
6天前
|
存储 算法 NoSQL
常见限流算法及其实现
在分布式系统中,随着业务量的增长,如何保护核心资源、防止系统过载、保证系统的稳定性成为了一个重要的问题。限流算法作为一种有效的流量控制手段,被广泛应用于各类系统中。本文将详细介绍四种常见的限流算法、两种常用的限流器工具,从原理、源码的角度进行分析。
162 0
|
6天前
|
监控 算法 Java
sentinel 服务限流工作原理
sentinel 服务限流工作原理