三种限流策略和代码实现

导读:本篇文章讲解 三种限流策略和代码实现,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1. 概述

在高并发访问的情况下,通常会通过限流的方式在控制流量访问问题,以保证服务处在正常压力之下,常见的限流策略有计数器限流、漏桶限流和令牌桶限流

2. 计数器限流

在一段时间间隔内(时间窗口),处理请求的最大数量固定,超过部分不做处理
代码实现如下

public class CounterServiceImpl implements CounterService {

    /**起始时间*/
    private long startTime = Instant.now().toEpochMilli();
    /**时间区间的时间将毫秒数*/
    private long interval = 1000;
    /**每秒限制数*/
    private long maxCount = 2;
    /**累加器*/
    private AtomicLong atomicLong = new AtomicLong();

    @Override
    public long tryAcquire(long taskId, int turn) {
        long nowTime = Instant.now().toEpochMilli();
        if (nowTime < startTime + interval) {
            long count = atomicLong.incrementAndGet();
            if (count <= maxCount) {
                return count;
            } else {
                return -count;
            }
        } else {
            synchronized (CounterServiceImpl.class) {
                if (nowTime > startTime + interval) {
                    atomicLong.set(0);
                    startTime = nowTime;
                }
            }
            return 0;
        }
    }
}

3. 漏桶限流

漏桶大小固定,处理速度固定,但请求进入的速度不固定,请求过多时,会丢弃多于的请求
限流规则如下

  1. 客户端请求以任意速率进入漏桶
  2. 漏桶的容量是固定的,放行请求速率也是固定的
  3. 如果处理请求速度太慢,桶内请求会超出桶容量,后面进入的请求就会溢出,即拒绝请求进入

代码实现如下

@Service
public class LeakBucketServiceImpl implements LeakBucketService {

    /**起始时间*/
    private long lastOutTime = Instant.now().toEpochMilli();
    /**流出速率每秒2次数*/
    private long rate = 2;
    /**剩余量*/
    private long water = 0;

    @Override
    public synchronized boolean tryAcquire(long taskId, int turn) {
        long nowTime = Instant.now().toEpochMilli();
        long pastTime = nowTime - lastOutTime;
        //漏出量 = 过去时间 * 速率
        long outWater = pastTime * rate / 1000;
        //剩余水量 = 上次遗留水量 - 漏出水量
        water = water - outWater;
        //纠正剩余水量
        if (water < 0) {
            water = 0;
        }
        //剩余水量小于等于1,则放行
        if (water <= 1) {
            lastOutTime = nowTime;
            water++;
            return false;
        } else {
            return true;
        }
    }
}

4. 令牌桶限流

令牌桶大小固定,令牌的产生速度固定,但是消耗令牌(请求)的速度不固定,每个请求都会从令牌桶中获取令牌,如果没有令牌,就丢弃这次请求
限流规则如下

  1. 程序安装某个速度向桶中放入令牌
  2. 令牌的容量是固定的,放行的速度是不固定的,只要桶中还有剩余令牌,一旦请求过来就能申请成功,然后放行
  3. 如果令牌的发放速度慢于请求到来的速度,桶内就没有令牌可以领取,新来的请求就会被拒绝

代码实现如下

@Service
public class TokenBucketServiceImpl implements TokenBucketService {

    /**上一次令牌发放时间*/
    private long lastTime = Instant.now().toEpochMilli();
    /**桶容量*/
    private int capacity = 2;
    /**令牌生成速度 个/秒*/
    private int rate = 2;
    /**当前令牌数*/
    private int tokens;

    @Override
    public synchronized boolean tryAcquire(long taskId, int applyCount) {
        long nowTime = Instant.now().toEpochMilli();
        //时间间隔,毫秒
        long gap = nowTime - lastTime;
        tokens = Math.min(capacity, (int) (tokens + gap * rate / 1000));
        if (tokens < applyCount) {
            return true;
        } else {
            tokens -= applyCount;
            lastTime = nowTime;
            return false;
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76739.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!