美文网首页
限流算法

限流算法

作者: 愤怒的老照 | 来源:发表于2020-03-06 11:20 被阅读0次

没有单位时间访问量要求

这种不需要考虑时间,只需要考虑线程安全就可以,用信号量可以解决

// 最大访问量为100;
    int maxRequestNum = 100;
    Semaphore handler = new Semaphore(maxRequestNum);

    public void request(){
        if (!handler.tryAcquire()){
            return ;
        }

        // 调用接口
    }

QPS限流(是一段时间内(一般指1秒)的请求个数。)

计数器法

这种方法是QPS中最简单的,以1秒为时间间隔,同一秒内请求数小于最大限制就可以执行,否则不执行,下一秒后清空请求数即可

// 最大访问量为100;
    int maxRequestNum = 100;
    Semaphore handler = new Semaphore(maxRequestNum);
    long timestap = System.currentTimeMillis(); // 每个时间段的第一个时刻
    long interval = 1000; // 时间间隔1000ms

    public void request(){
       long now = System.currentTimeMillis();
       if (now < timestap + interval){
           if (handler.tryAcquire()){
               // 执行业务
           }else {
               return ;
           }
       }else {
           // 初始化下一个时间段
           timestap = now;
           handler = new Semaphore(maxRequestNum);
           // 执行业务
       }
    }

这种方法的缺点也是很明显的,粒度比较粗,假如一秒内的最大访问请求数量为100个,用户在第一毫秒就将这些请求全部使用,则这一秒内剩下的请求将全部不处理。

滑动窗口

1.png

这个是网上找的一个图,滑动窗口的思想就是将粒度细化,比如上图将1秒分为4分,每过0.25秒窗口像右滑动1/4,也就是每0.25秒腾出对应的访问数量。假如每秒最大访问量为100次,第1秒的后0.75秒和第二秒的前0.25秒访问量共100次,用计数法第二秒的后0.75秒将无法访问,如果使用滑动窗口的话,将会清空第一窗口的数量,使系统能再次访问。

    // 最大访问量为100;
    int maxRequestNum = 100;
    AtomicInteger[] handlers = new AtomicInteger[4];
    AtomicInteger sum;
    int index = 0;
    public void init(){
        // 初始化
        Arrays.stream(handlers)
                .map(item -> {
                   return new AtomicInteger(0);
                });
        sum = new AtomicInteger(0);
    }

    public synchronized void request(){
        // 对应窗口的访问量++;
        handlers[index].incrementAndGet();
        if (sum.incrementAndGet() < maxRequestNum){
            // 执行业务
        }else {
            // 限流
        }
    }

    //窗口滑动,每0.25秒执行一次
    public void slide(){
        // 窗口滑动
        index = (index + 1) % handlers.length;
        //对应窗口数量清空
        int count = handlers[index].getAndSet(0);
        // 窗口滑动,整体数减去窗口对应的数量
        sum.addAndGet(count);
    }

漏桶算法

2.png

这种方法是将水(请求)滴到桶中,桶中的水(请求)以固定速率漏出去,当水溢出来时,不处理请求,桶没有满时,处理请求。因为这种方法是固定速率的,所以没有计数器法中的边界。我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。

long timestamp = System.currentTimeMillis();
    int capacity; // 桶的容量
    int rate; // 水流出来的速率
    long water; //桶中的水数量

    public void request(){
        long now = System.currentTimeMillis();
        // 速率固定,可以使用相减来获取水的数量
        water = Math.max(0, water - (now - timestamp) * rate);
        if (water < capacity){
            water ++;
            // 执行请求
        }else {
            // 限流
        }
    }

令牌桶

3.png

为啥俺感觉这俩作用是一样的呢?网上说的令牌桶算法可以解决突发访问难道是加一个缓冲区???我来贴解释了https://www.cnblogs.com/xrq730/p/11025029.html
,可以使用Guava-RateLimiter

    long timestamp = System.currentTimeMillis();
    int capacity; //令牌桶容量
    int  rate; // 放入令牌的速率
    long sum; //当前令牌的数量

    public void request(){
        long now = System.currentTimeMillis();
        sum = Math.min(capacity, sum + (now - timestamp) * rate);
        timestamp = now;
        if (sum < 1){
            // 限流
        }else {
            sum -= 1;
            // 执行业务
        }
    }

实战

为了方便使用,通常封装成注解,使用切面来完成

Limit注解:
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit {
}

切面类
@Aspect
@Component
@Slf4j
public class LimitAspect {

    private static final int limit = 100;
    private ConcurrentHashMap<String, RateLimiter> handler = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.whut.insurance.common.annotation.Limit)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
        // 获取许可。如果有返回true,否则返回false
        Signature sig = joinPoint.getSignature();
        MethodSignature msig = (MethodSignature) sig;
        Object target = joinPoint.getTarget();
        boolean token = false;
        try {
            Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
            if (handler.containsKey(currentMethod.getName())){
                token = handler.get(currentMethod.getName()).tryAcquire();
            }else {
                RateLimiter rateLimiter =  RateLimiter.create(limit);
                handler.put(currentMethod.getName(), rateLimiter);
                token = rateLimiter.tryAcquire();
            }
        } catch (NoSuchMethodException e) {
            log.error("get method error[{}]", e.getMessage());
        }
        System.out.println(handler.toString());
        Object obj = null;
        try {
            if (token) {
                obj = joinPoint.proceed();
            } else {
                ResponseWriter.println(response, ServerResponse.createByErrorCodeMessage(
                        ResponseCode.FREQUENT_VISIT.getCode(),
                        ResponseCode.FREQUENT_VISIT.getDesc()
                ));
            }
        } catch (Throwable e) {
            log.error("get method error[{}]", e.getMessage());
        }
        return obj;
    }

}

使用
    @Limit
    @PostMapping("/captcha")
    public void captcha(@RequestBody @Valid RandomKeyParam randomKey, HttpServletResponse response) throws IOException, IOException {}

相关文章

  • 限流框架系列之常见限流算法

    四种常见的限流算法 固定时间窗口限流算法 滑动时间窗口限流算法 令牌桶限流算法 漏桶限流算法 算法比较 算法确定参...

  • 高并发下的Nginx实用配置 - 限流和黑白名单

    1. 限流算法 漏桶算法 令牌桶算法 更多限流算法相关知识,请查看【单机限流 - 限流算法及隔离策略】[https...

  • 网关限流实例

    描述 限流是指将处理请求数限定在单位时间的阀值内。常用的限流算法固定时间窗口限流算法和滑动时间窗口限流算法。固定时...

  • 高并发环境下的限流策略

    本文将从以下几个方面分析限流策略: 什么是限流限流算法限流算法的应用 什么是限流 在开发高并发系统时,有很多手段来...

  • 基础架构 | 限流算法

    限流算法 令牌桶算法 漏桶算法

  • 2020-06-09

    目录 代理层限流 容器限流 API 限流 时间窗口 漏桶算法 令牌桶算法 总结 为了保护暴露在公网...

  • Redis+Lua脚本三步实现分布式系统限流

      在分布式系统中,说到限流方案我们一般会使用redis结合限流算法来做,一般的限流算法有令牌桶算法、漏桶算法、固...

  • 分布式架构

    大流量限流/削峰 常见的限流算法 计数器算法池化资源技术的限流就是通过计数器算法来控制全局的总并发数。 令牌桶算法...

  • 限流算法简介及Guava RateLimiter令牌桶限流介绍

    参考 常用4种限流算法介绍及比较 超详细的Guava RateLimiter限流原理解析 限流算法简介 1.计数器...

  • 高可用实践-限流算法

    限流算法 常见的限流算法有计数器算法、漏桶算法和令牌桶算法。 计数器法 计数器算法“简单粗暴”。该算法会维护一个c...

网友评论

      本文标题:限流算法

      本文链接:https://www.haomeiwen.com/subject/iagvrhtx.html