限流算法

常见的限流算法有两种

  1. 漏桶算法(LeakyBucket)
  2. 令牌桶算法(TokenBucket)

漏桶算法

漏桶算法_百度百科

漏桶算法(Leaky Bucket)是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。

令牌桶算法

令牌桶算法_百度百科

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

二者的区别

漏桶算法能够强行限制数据的传输速率。

令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。

在实际的应用中,nginx上接受的流量不可能一直平稳,必要的时候需要允许一定量的突发请求保证用户正常访问,所以限流策略上令牌桶算法将比漏桶算法更具实用性

配置方法

无论是漏桶还是令牌桶,在nginx上一般定义在http配置块中,常见的例子如下:

limit_req_zone $server_name zone=mylimit:10m rate=10r/s;

在这个配置中,我们定义了一个名为mylimit的限流域,该限流域将会申请10M的共享内存,针对指定的主机名$server_name生效,限制速率为每秒10次请求,每次次请求间隔100ms.

10M的共享内存用来存储访问的频次信息,1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。

定义配置后需要应用在实际使用的虚拟主机上,可针对location进行配置或直接配置在server中,我们可以根据配置的不同选择使用漏桶算法或者令牌桶算法.

空桶

limit_req zone=mylimit;

由于我们在limit_req_zone中已经定义了流速rate,那么如上配置可以使ngixn上的流量按照指定的流速流入

若流入流量大于指定速率,将会被nginx直接拒绝,并返回错误代码503

这种配置方法虽然可以平滑流入nginx的流量,但是无法应对突发状况,应对突发请求时将会造成较多流量请求失败,nginx本身也没有任何机制缓存请求.所以这种配置方法被称为"空桶"

假设在此配置下有100个同时请求到达nginx,那么nginx在处理完第一个请求后会将处理间隔(100ms)中的请求全部拒绝,由此可见此中配置方法无法应对突发请求.

漏桶

漏桶算法在空桶基础上会多一个队列burst,用于缓存超过流速的请求

limit_req zone=mylimit burst=100;

在此配置中,定义了一个容量为100的队列,用于缓存请求

已缓存的请求,将会在队列中以mylimit中指定的速率进行处理

若请求流入过高,超过了burst中预设的100的值,将会被nginx拒绝,并返回503

假设在此配置下有突发的101个请求到达nginx,那么第一个请求会被nginx马上处理,剩余的100个请求将会被缓存在队列里,按照10r/s的速率处理.若此时有更多的请求,也会被nginx拒绝并返回503

依旧是上面的假设,由于nginx处理的速率为10r/s,虽然此时101个请求都会被nginx处理,但由于队列是排队执行的,第101个请求被nginx执行时将会等待10秒,如果对于请求时间有较高要求的话,这样的延迟无疑是致命的

由此可见,漏桶算法配置对于突发请求还是有一定的局限性

令牌桶

严格意义上说此种配置方法并不完全是令牌桶,可理解为在原有漏桶的基础上有处理突发请求的兼容做法

limit_req zone=mylimit burst=100 nodelay;

这个配置在漏桶算法的基础上增加了nodelay参数,这意味着burst队列中100个请求将会被nginx立刻处理,相比起漏桶算法,可以应对突发请求

但即使burst队列中的请求已经被全部处理了,burst队列并不会立刻被清空,而是按照指定的速率10r/s慢慢释放,释放之后才可以缓存新的请求.新请求缓存进桶的速率与桶释放队列的速率相同.

假设在此配置下有突发的101个请求到达nginx,那么这101个请求将会被nginx同时处理.处理完毕后,burst队列中将会以10r/s的速率释放桶里的队列,那么后接收的请求也将以10r/s的速率被nginx处理.如果此时再有突发请求,超过了制定的速率,那么这部分请求将会被nginx拒绝,并返回503

令牌桶的优化

虽然令牌桶的配置面对突发请求会有一定的作用,但由于nodelay的配置下,可能会使nginx的流入请求不均匀.

假设nginx上有一段时间没有流量,此时桶里没有缓存任何请求,突然来一波大流量,导致桶满,而桶里的请求又会被全部处理,处理完毕后又有一段时间没有流量过来......那么此时的nginx流量就显得不是那么均匀.

因此我们可以再对配置进行一下优化

limit_req zone=mylimit burst=100 delay=50;

在这个配置下,突发请求导致burst桶满的情况下,nginx只会同时处理50个请求,剩余的50个请求将会按照设定速率进行处理,多余的请求将会被拒绝.那么就可以兼顾nginx流速均匀以及突发请求的问题

拒绝请求HTTP代码

通过上面各种漏桶配置我们不难看到,超过流量的部分都会被nginx拒绝并返回503,那如果nginx作为反向代理工具,后端服务器也可能返回503的状态下,我们不太可能直观的观察出这个503到底是后端服务器返回的还是nginx限流返回的,对此我们可以修改nginx在超出限流返回的默认值

limit_req zone=mylimit burst=100 delay=50;
limit_req_status 512;

这样配置的话,nginx在超出限流拒绝并返回的http代码就为512,由此与其他的状态进行区分.

标签: nginx