首页IT科技限流法优点(常见限流算法)

限流法优点(常见限流算法)

时间2025-09-19 13:04:23分类IT科技浏览5059
导读:简介 限流顾名思义是对流量大小进行限制,防止请求数量超过系统的负载能力,导致系统崩溃,起到保护作用。...

简介

限流顾名思义是对流量大小进行限制               ,防止请求数量超过系统的负载能力                        ,导致系统崩溃        ,起到保护作用                。

现实生活中限流也随处可见       ,节假日出门旅行的人数会剧增                        ,对于旅游景点来说往往会不堪重负                ,如果不进行人数控制       ,对整个景点的压力会非常大                       ,游客的体验也会非常差                ,还容易出现安全事故等危险                       。

同样的在一线城市地铁限流也非常常见,早高峰为了控制乘车人数和有序进站                       ,地铁往往会在地铁口进行拦截                        ,一定时间内才放行一部分人进站乘车        。

具体到程序,限流可以有以下几种场景

限制某个接口每秒最多访问多少次 限制某个ip每秒最多访问多少次 限制某个用户或某个来源每秒最多访问多少次 限制某些用户下载速度每秒最多多少kb 禁止某些用户或ip的访问

限流起到了保护作用               ,那么如何限呢?如果限制得太严                        ,保护是保护到了        ,但是系统的处理能力下降了               ,体验会很差;如果限制得太松                        ,就会被一些突增流量冲击到        ,或者被黑客利用进行安全攻击                。如何限流需要根据系统的负载来评估       ,系统的负载和处理能力是动态的                        ,例如平时的qps是1000                ,双11一般会进行扩容       ,也就是加服务节点                       ,qps可能就是5000                ,这个时候系统处理能力变强的,限流策略也应该相应的调整                       。还有一种是出于安全的限流                       ,例如同一个客户端ip 1s内对系统发出上万次请求                        ,这种可以确定就是安全攻击,很可能是有人恶意破坏               ,或者是一些爬虫                        ,这种可以限制请求数        ,超出的就直接拒绝        。

如何限流是限流算法要实现的               ,常见的限流算法有“两桶两窗               ”                        ,固定窗口                、滑动窗口                       、漏桶与令牌桶        ,接下来介绍这四种算法及应用        。

固定窗口

固定窗口的思想和实现非常简单       ,就是统计每个固定每个时间窗口的请求数                        ,超过则拒绝                       。

如图我们定义了窗口大小为1s                ,最大请求数100       ,窗口内超过100的请求数将被拒绝               。实现上也非常简单                       ,利用redis的incr自增计数                ,当前时间秒作为缓存key,每次自增后判断是否超过指定大小即可        。

固定窗口的问题是容易出现“突刺现象                        ”                       ,例如在1s内                        ,100个请求都是在前100ms过来的,那么后面的900ms的请求都会被拒绝               ,而系统此时是空闲的                        。另外还有“临界问题        ”                        ,如果100个请求是在后100ms过来的        ,而下一个1s的100个请求在前100ms过来               ,此时系统在这200ms内就需要处理200个请求                        ,跟我们想要的不符合               。到这里我们很容易想到        ,1s这个范围太大了       ,缩小一些就更好了                        ,这种把固定窗口拆成更多个小窗口的做法就是滑动窗口算法了。

滑动窗口

滑动窗口的思想是将固定窗口拆成更多个小窗口                ,随着时间的推移       ,窗口不断的滑动                       ,统计也在不断的变化                        。窗口拆分的越多                ,滑动就会越平滑,统计就会越精确                       ,所消耗的资源就会越多                       。滑动窗口如果只拆为1个窗口                        ,就退化为固定窗口。

滑动窗口算法可以解决上面固定窗口的问题,像hystrix和sentinel中都使用该算法进行数据统计               ,用于限流熔断等策略处理                。如hystrix中图所示                        ,在一个窗口内拆分了10个桶(bucket)        ,随着时间的推移               ,会创建新的桶也会丢弃过期的桶                        ,当然窗口的大小和拆分桶的数量都是可配置的                       。

漏桶

漏桶算法的思想是将请求先放到一个桶中        ,然后像滴水一样不断的从中取出请求执行       ,桶满则溢                        ,后面的请求会被拒绝        。

漏桶算法的特点是流入速度不确定                ,但是流出速度是确定的       ,漏桶可以很平滑                       ,均衡的处理请求                ,但是无法应对短暂的突发流量                。

令牌桶

令牌桶算法的思想是不断的生成令牌放到一个桶中,请求到来时到桶中申请令牌                       ,申请得到就执行                        ,申请不到就拒绝                       。如果桶中的令牌满了,新生成的令牌也会丢弃        。

与漏桶不同的是               ,令牌桶是流入速度确定(生成令牌的速度)                        ,流出速度不确定        ,所以它不想漏桶一样可以均衡的处理请求               ,但是由于有令牌桶这个缓冲                        ,一旦有突增的流量        ,令牌桶里已有的令牌可以短暂的应对突发流量       ,由于流出速度是不限制的                        ,此时桶中已有的令牌都可以被申请到                ,请求一下子就会到我们的服务       ,给系统带来一定的压力                       ,所以桶的大小需要合适                ,不宜过大        。举个栗子:令牌桶的大小是1000,每秒放100个令牌                       ,经过一段时间后                        ,请求有空闲时,桶里的令牌就会积压               ,最终保存了满1000个令牌                        ,由于某刻流量突增        ,有1000个请求到来               ,此时能申请到1000个令牌                        ,所有请求都会放行        ,最终达到我们的系统       ,如果令牌桶过大                        ,系统可能会承受不了这波请求                       。

应用

guava RateLimiter

guava限流实现的是桶算法                ,通过RateLimiter.create创建       ,可以创建两种类型的限流器                       ,SmoothBursty和SmoothWarmingUp                ,前者定时生成令牌,后者有一个预热的过程               。

我们如下示例代码                       ,每秒会创建2个令牌                        ,并且初始化的时候就是2        。定时器每200ms会申请一次令牌,每秒申请5次               ,只有2次成功                        ,所有运行程序每秒有3次success和2次fail                        。 RateLimiter rateLimiter = RateLimiter.create(2); new Timer().schedule(new TimerTask() { @Override public void run() { if (rateLimiter.tryAcquire()) { System.out.println("success"); } else { System.out.println("fail"); } } }, 0, 200);

既然是桶        ,那么桶的大小是多少呢?SmoothBursty里最大令牌数由maxPermits字段表示               ,该字段等于maxBurstSeconds * permitsPerSecond                        ,permitsPerSecond是每秒要生成的令牌数        ,maxBurstSeconds默认是1               。

另外还可以创建SmoothWarmingUp带有预热功能的限流器       ,预热的作用是通过一个过程才达到permitsPerSecond                        ,相当于让系统有个热身的时间。 RateLimiter rateLimiter = RateLimiter.create(5, 10, TimeUnit.MILLISECONDS); new Timer().schedule(new TimerTask() { @Override public void run() { log.info("" + rateLimiter.acquire()); } }, 0, 200);

rateLimiter.acquire()返回的是获取打令牌的时间                ,运行程序可以看到开始并不是每秒都能产生5个令牌       ,也就是不是能立刻获取到令牌                       ,获取令牌需要的时间会越来越小                ,直到预热期过后就能立马获取到令牌了                        。

guava的限流只能提供单机版的实现,对于集群就无能为力了                       ,并且它通常作为一个工具存在                        ,使用还需要自己封装,集成到服务               ,并不能开箱即用                       。

bucket4j

bucket4j是一个java实现                        ,基于令牌桶算法的限流组件。它支持分布式限流        ,支持多种存储               ,可以方便与各种框架和监控集成                。github上start 1.2K                        ,但是issues数量少        ,国内估计使用的人也不多       ,并且官方的实现存储不支持最常用的redis                        ,它专注于限流                ,如果是自研或者二次开发       ,是一个很好的参考                       。

Resilience4j

之前我们介绍过它的熔断功能                       ,Resilience4j也提供了限流的实现                ,可以参考这里        。相比guava,Resilience4j是框架级别的                       ,可以很方便的使用                。但Resilience4j也是单机版的实现                        ,无法支持集群功能                       。

Resilience4j限流实现的是令牌桶,如下配置               ,每1s会生成10个令牌        。 resilience4j.ratelimiter: instances: backendA: limitForPeriod: 10 limitRefreshPeriod: 1s timeoutDuration: 0 registerHealthIndicator: true eventConsumerBufferSize: 100

sentinel

流量控制是sentinel最重要的一个功能                        ,sentinel属于后起之秀        ,文档齐全               ,支持的场景更加丰富        。sentinel支持集群限流                        ,也可以像guava一样预热        ,还可以基于调用链路进行限流                       。

sentinel还提供了控制台功能       ,支持多种数据源的持久化                        ,使用spring cloud的话可以通过spring cloud alibaba引入sentinel               。

开源版的sentinel有一些限制                ,并且使用起来并不是那么方便       ,例如Resilience4j可以配置一个default针对所有的请求生效                       ,但sentinel需要单个单个url去配置                ,显得非常麻烦,包括熔断feign接口的配置也是                       ,这个给spring cloud alibaba提了feature                        ,也许在下一个版本就会提供支持        。

nginx

上面讲到的都是应用级别的限流,nginx通常作为网络请求的入口               ,从运维的角度来说                        ,在这里做限流再合适不过        ,nginx本身也提供了限流的支持                        。

nginx比较适合对外的限流               ,但是我们内部不同系统间的调用一遍不经过nginx                        ,会直接访问到对方的网关        ,所以两者并不矛盾               。

nginx限流通过limit_req和limit_conn两个模块支持       ,分别对应请求限制和链接限制(一个链接可以有多个请求)。 http { limit_req_zone zone=name:10m rate=100r/s; server { location /app/ { limit_req zone=name burst=500 nodelay; } }

如上                        ,定义了一个name zone                ,访问速率最高是100个每秒       ,/app路径应用了这个规则                        。busrt表示爆发的意思                       ,是一个缓冲队列                ,用于存储突增的请求,这些请求会被缓存不会拒绝                       ,如果超过了burst                        ,nodelay表示不等待直接拒绝                       。

前面我们说到有些恶意攻击可能每秒发送上万个请求,导致服务崩溃               ,如果多个应用系统共用一个nginx                        ,那么可以统一在nginx配置处理        ,不需要每个系统自己去实现。 limit_conn_zone $binary_remote_addr zone=name:10m; server { limit_conn name 50; }

如上               ,定义了一个name zone                        ,$binary_remote_addr表示远端地址        ,也就是ip       ,10m表示存储空间                        ,10m大概可以存储16w的ip地址                ,我们在server节点应用这个规则       ,50表示最多50个                       ,超过就会拒绝                。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
linux如何修改文本内容(Linux中安装使用semanage来修改文本的教程)