美文网首页案例
限流和常见的三种算法

限流和常见的三种算法

作者: 黄靠谱 | 来源:发表于2019-07-10 17:14 被阅读61次

    限流的三种算法
    https://www.cnblogs.com/forezp/p/10140316.html

    概述

    限流要解决的问题

    1. 防止QPS过高导致的服务器崩溃,特别是一些耗时、耗资源的操作,限流可以保护服务器负载可控

    2. 基于业务上考虑(比如IP限流、用户限流 ),限流可以避免业务上的异常运行

    • 防止用户异常的操作,例如开挂抢商品、抢火车票
    • 防止攻击服务器,导致正常的请求没法及时处理,DDOS攻击

    典型限流的应用场景:

    • 秒杀 防止刷单
    • 耗资源的一些操作(比如微信token的限流,因为是 1对多,不限流 容易崩)

    如何限流?
    一般网关都有这种功能。 gateway、nginx、zuul等

    限流:一定时间内,只允许N次请求。
    从计算机友好的角度出发,是希望能在单位时间内均摊掉请求,使用漏斗算法可以达到这种效果。
    但是漏斗算法有个弊端,就是先快后慢的这种请求,那么峰值的请求也只能排队等待被消费。实际上计算机是具备一定的高并发处理能力的,只要不是一直处于高并发下即可。所以 计数器限流和 漏洞限流折中的算法,令牌限流成为现在最主流的算法。

    方案一:计数器限流

    (Redis 结合expire方案可以实现)
    第一次请求开始计时,例如1s以内,达到100次请求就拒绝访问了,直到1s过后,重新开始计数。

    优点:

    • 实现简单
    • 可以保证一定时间内,一定能处理完限流次数的请求。比如1s限流1000次请求,那么无论是先快后慢,还是先慢后快,都可以完成1000次请求。

    缺点:短暂的峰值过高对服务器不友好。服务器希望能把请求尽量的均摊开来,这样可以充分利用计算机资源。

    • 先到先得,可能1ms就有1000次请求打满了,999ms里面都是空闲,对服务器而言,这样并不友好。
    • 而且1s的最后1ms有1000次请求,下一秒开始头1ms又有1000次请求,会导致连续的峰值过高,对服务器特别不友好。

    方案二:漏洞限流

    消费的速度是恒定的,对于服务器而言是最友好的。
    在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池(ScheduledExecutorService)来定期从队列中获取请求并执行,可以一次性获取多个并发执行。
    参数:消费速度、桶容量(超过就抛弃,可以避免内存过大,有过多的等待的任务)

    优点:

    1. 实现限流的功能的同时,又可以充分利用计算机资源,非常友好的一种方案
    2. 不会因为开始计数时间导致异常的峰值

    缺点:

    1. 并不能保证规定时间内消费预期的流量,比如1s内限流1000次,1ms1个请求的模型,如果前面没有请求,第900ms时有大量的请求,那么1s之内的处理速度时100个。
    2. 先快后慢的时候,比如前100ms有500个请求,后900ms有500个请求。1s也是1000个请求,但是前500个请求只能排队,1ms处理一个,实际上计算机是有一定的高并发能力的,只要不是长期处于高并发的模式都行的。这样前面快的请求不需要盲目等待。

    方案三:令牌限流

    令牌桶算法是比较常见的限流算法之一,大概描述如下:
    1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
    2)根据限流大小,设置按照一定的速率往桶里添加令牌;
    3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃活着拒绝;
    4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
    5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;

    这种算法,既可以保证系统由一定的高并发能力,比如当前令牌桶容量是100,一开始直接消费掉100个请求。有保证服务器不会因为短暂的爆发,而导致server端空闲,因为令牌桶还会持续的生产令牌。

    既有一定的并发能力,又不至于完全失去控制,可控的兼具并发力和流量控制的限流算法.是计数器算法(一定的并发处理能力)和漏洞限流(高峰过后仍然会持续的产生令牌)的折中算法。

    Gateway的限流

    • 针对用户限流
    • 针对url限流
    • 针对IP限流
        @Bean
        KeyResolver userKeyResolver() {
            return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
        }
    
        @Bean
        public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
        }
    
        @Override
        public Mono<String> resolve(ServerWebExchange exchange) {
            return Mono.just(exchange.getRequest().getURI().getPath());
        }   
    

    相关文章

      网友评论

        本文标题:限流和常见的三种算法

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