Sentinel限流算法:滑动时间窗算法、漏桶算法、令牌桶算法。拦截器定义资源实现原理



滑动时间窗算法

基本知识

限流算法最简单的实现就是使用一个计数器法。比如对于A接口来说,我要求一分钟之内访问量不能超过100,那么我们就可以这样来实现:

  • 最开始的时候就设置一个count值,每当一个请求过来我就count++
  • 如果count的值大于了100,并且与第一个请求的时间间隔小于1分钟,那么就表示请求数过多
  • 如果该请求与第一个请求的间隔时间大于1分钟,且count的值还在限流范围内,那么就重置count。

在这里插入图片描述

/**
 * @Description: 限流算法之计数器法,伪代码的实现
 * @Author 胡尚
 * @Date: 2024/7/12 8:50
 */
public class Count {
   
    // 当前时间
    private Long timeStamp = System.currentTimeMillis();
    // 请求数量
    private int count = 0;
    // 时间窗口的最大请求数
    private final int limit = 100;
    // 时间窗口ms
    private final long interval = 1000 * 60;

    /**
     * 限流校验,校验是否成功
     * @return true表示允许请求,false表示校验未通过
     */
    public boolean check(){
   
        long now = System.currentTimeMillis();
        // 还在一分钟 时间窗口之内
        if (now <= timeStamp + interval){
   
            count++;
            return count <= limit;
        } else {
   
            timeStamp = System.currentTimeMillis();
            // 超时后重置请求数
            count = 1;
        }

        return true;
    }
}



计数法的实现有一个缺点,那就是限流的时间精度不准确。比如第一个时间窗口的前半分钟只有十个请求,后半分钟有90个请求;第二个时间窗口的前半分钟有90个请求,后半分钟只有十个请求。这种情况下,限流器是不会出现限流的,但是我在在一个时间段中的请求却有180个了。

在这里插入图片描述



为了解决计算器法统计时间精度不够的问题,进而引入了滑动时间窗

滑动时间窗算法的基本原理是维护一个固定大小的时间窗口,‌窗口内的数据被认为是当前分析的有效数据。‌随着时间的推移,‌新的数据点进入窗口,‌而旧的数据点则被移出窗口,‌从而形成了一个滑动的时间窗口。‌

在这里插入图片描述


在上图中,一个红色窗格就是一个时间窗口,我们把一个时间窗口分为了6个小格子。就拿时间窗口为一分钟举例,上面每一个小格子就是10秒;每过10秒,我们的时间窗口就会往右移动一格。每一个格子都有自己的计数器count,比如当一个请求 在0:35秒的时候到达,那么0:30~0:39对应的counter就会加1。



在具体实现上,‌滑动时间窗算法可以通过多种数据结构来实现,‌例如使用环形数组、‌哈希表等。‌例如,‌可以使用一个环形数组来存储时间窗口内的数据点,‌数组的大小等于时间窗口的大小(‌以时间单位为单位)‌。‌每当有新的数据点进入时,‌旧的对应时间点的数据将被覆盖,‌从而实现滑动时间窗的效果。‌此外,‌还可以使用哈希表来记录每个时间点上的数据变化情况,‌其中键为时间点,‌值为该时间点的数据值或变化量。‌



/**
 * @Description: 限流算法之滑动时间窗算法的伪代码实现
 * 假设每秒的请求不能超过100,我们设置一个1s的时间窗口,时间窗口中共有10个小格子,
 * 每个格子记录100ms的请求数,每100毫秒移动一次,每次移动都需要记录当前服务请求数
 * 我这里就简单实现,只有一个计数器,最新的小窗口永远存储最新访问请求总数。当然也可以每个小窗口都有自己的计数器。
 * @Author 胡尚
 * @Date: 2024/7/12 9:23
 */
public class SlidingTimeWindow {
   

    /**
     * 服务器访问次数,可以放在redis中实现分布式系统访问
     */
    private Long count = 0L;
    /**
     * 滑动时间窗,使用linkedList来记录滑动窗口的10个格子
     */
    private LinkedList<Long> slots = new LinkedList<>();

    public static void main(String[] args) throws InterruptedException {
   
        SlidingTimeWindow timeWindow = new SlidingTimeWindow();

        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                try {
   
                    timeWindow.onCheck();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        }).start();

        // 模拟一直都有请求数,随机休眠几毫秒
        while(true){
   
            // TODO 限流校验
            timeWindow.count++;
            Thread.sleep(new Random().nextInt(15));
        }

    }

    private void onCheck() throws InterruptedException {
   
        while (true){
   
            // 把当前访问总数存入时间小窗口中
            slots.add(count);

            // 时间窗口的剔除操作
            if (slots.size() > 10){
   
                slots.removeFirst();
            }

            // 最新的时间小窗口的数和最老的时间小窗口数进行比较,是否要限流
            if (slots.peekLast() - slots.peekFirst() > 100 ){
   
                // TODO 限流标识
            } else{
   
                // TODO 解除限流标识
            }

            Thread.sleep(100);
        }
    }


}



源码算法分析

我们接下来看看Sentinel它的滑动时间窗是怎么实现的。我们从StatisticSlot类的entry()方法开始看,因为它每次都会记录请求通过/请求拒绝相关的计数

public void entry (.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值