三种限流策略和代码实现

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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值