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

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

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

简介

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

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

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

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

限制某个接口每秒最多访问多少次 限制某个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系统容易被攻击吗(如何确定我的Mac或Linux是否易受到Shellshock攻击?)