滑动时间窗口算法测试(Sentinel)

Sentinel 官网地址 :官网地址

算法实现类

在这里插入图片描述

获取&初始化窗口 leapArray.currentWindow()

 public WindowWrap<T> currentWindow(long timeMillis) {
        if (timeMillis < 0) {
            return null;
        }
        //计算出位于那个窗口
        int idx = calculateTimeIdx(timeMillis);
        // Calculate current bucket start time.
        long windowStart = calculateWindowStart(timeMillis);

        while (true) {
            //根据下标获取对应窗口
            WindowWrap<T> old = array.get(idx);
            //初始化窗口
            if (old == null) {
                WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
                //可能多线程竞争CAS加锁赋值 返回窗口
                if (array.compareAndSet(idx, null, window)) {
                    // Successfully updated, return the created bucket.
                    return window;
                } else {
                    // Contention failed, the thread will yield its time slice to wait for bucket available.
                    Thread.yield();
                }
            } else if (windowStart == old.windowStart()) {
                //在当前窗口内 直接返回
                return old;
            } else if (windowStart > old.windowStart()) {
                //下一个周期 更新当前窗口时间,重置数据 实现窗口复用
                if (updateLock.tryLock()) {
                    try {
                        // Successfully get the update lock, now we reset the bucket.
                        return resetWindowTo(old, windowStart);
                    } finally {
                        updateLock.unlock();
                    }
                } else {
                    // Contention failed, the thread will yield its time slice to wait for bucket available.
                    Thread.yield();
                }
            } else if (windowStart < old.windowStart()) {
                // Should not go through here, as the provided time is already behind.
                return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
            }
        }
    }

链路调用判断是否限流

@Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
        //获取过去时间内的有效请求数
        int curCount = avgUsedTokens(node);
        //过去时间内的有效请求数 +1 是否大于限流数 
        if (curCount + acquireCount > count) {
            if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {
                long currentTime;
                long waitInMs;
                currentTime = TimeUtil.currentTimeMillis();
                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;
    }

      private int avgUsedTokens(Node node) {
        if (node == null) {
            return DEFAULT_AVG_USED_TOKENS;
        }
        return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());
    }

 
    public double passQps() {
        return rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec();
    }

    public long pass() {
        data.currentWindow();
        long pass = 0;
        //data.values 返回有效数据 当前时间减去窗口开始时间大于统计时间的丢弃
        //三个窗口开始时间 100 500 1000  当前 1600 则 窗口100,500丢弃不纳入请求数统计 1600-100>1000,1600-500>1000
        List<MetricBucket> list = data.values();

        for (MetricBucket window : list) {
            pass += window.pass();
        }
        return pass;
    }

测试代码

public static void main(String[] sre) throws InterruptedException {
        long count = 2, totalPass = 0;
        //初始化一个滑动窗口 5s 分为5个窗口 每个窗口1s
        BucketLeapArray leapArray = new BucketLeapArray(5, 1000*5);
        ArrayMetric arrayMetric = new ArrayMetric(leapArray);

        leapArray.currentWindow().value().addPass(1);
        //线程沉睡唤醒需要消耗时间 测试时选择较大时间来减少误差
        Thread.sleep(1000);
        leapArray.currentWindow().value().addPass(1);
        Thread.sleep(1000);
        leapArray.currentWindow().value().addPass(1);
        Thread.sleep(1000);
        leapArray.currentWindow().value().addPass(1);

        totalPass = arrayMetric.pass();
        leapArray.debug(System.currentTimeMillis());
        if (totalPass+1>count){
            //something... 限流了
        }
        System.out.println("通过请求数"+totalPass);

        Thread.sleep(2100);
        //窗口  1000 2000 3000 4000 5000  当前6100 pass()时触发 leapArray.currentWindow() 满足条件windowStart > old.windowStart() 重置时间,重置数据
        totalPass = arrayMetric.pass();

        //对比两次debug输出 可以发现第二次有一个窗口时间被更新了,实现了窗口的复用统计
        leapArray.debug(System.currentTimeMillis());
        System.out.println("通过请求数"+totalPass);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值