在Nginx中配置限流

本文详细介绍了Nginx的流量限制配置,包括漏斗算法原理、基本配置、处理突发情况以及无延迟排队的实现。通过limit_req_zone和limit_req指令限制请求速率,保护服务器免受DDoS攻击,确保系统稳定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 🍀 nginx流量限制的工作原理
  2. 🍀 nginx限流的基本配置
  3. 🍀 处理突发情况
  4. 🍀 无延迟排队

🍃流量限制(rate-limiting)是nginx最有用的功能之一,却经常被错误理解和错误配置。它允许我们限制用户在给定时间内可以发出的HTTP请求数量。例如请求网站首页的GET请求,表单登录的POST请求等。

🍃速率限制可以出于安全目的使用。例如,可以降低暴力破解账号密码的攻击速度。通过将传入请求速率限制为实际用户的典型值,并(通过记录)标识目标URL,它可以帮助我们防御DDOS攻击。而更为通用的用法是,防止上游应用程序服务器同时被太多用户请求所淹没。

1. nginx流量限制的工作原理

🍂 流量限制(rate-limiting)使用的是漏斗算法leaky bucket algorithm,该算法在带宽受限的情况下广泛用于电信和分组交换计算机网络中,以处理突发性问题。类比是用一个水桶,在水桶的顶部浇水,然后从底部漏水。如果倒水的速度超过漏水的速度,则水桶会溢出。在请求处理方面,水代表来自客户端的请求,存储桶代表队列,根据先进先出(FIFO)调度算法,请求等待处理。漏水表示离开缓冲区以供服务器处理的请求,溢出表示已丢弃且从未得到服务的请求。

2. nginx限流的基本配置

🍂 限流主要依靠2个指令来配置limit_req_zonelimit_req,配置如下范例👇

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
    location /login/ {
        limit_req zone=mylimit;
        proxy_pass http://my_upstream;
    }
}
  • limit_req_zone指令定义了限流的具体配置,limit_req在上下文中启用上述配置(此范例中,针对/login/的请求都将受到mylimit配置的影响)

  • limit_req_zone指令,它通常定义在http区块中,在多个server上下文中都可以使用,其包含3个参数,解释如下👇

    Key:定义限制请求时提取的辨认特征。在范例中,我们使用的是nginx变量$binary_remote_addr,该变量表示的是客户端IP地址的二进制表示,这意味着第三个参数:请求速率的判断条件是IP的二进制表示。(当然我们也可以使用IP的字符串表示$remote_addr,但是它占用的空间大于二进制)

    zone=name:size:定义共享内存区域大小,用于存储每个IP地址状态以及限制请求URL的访问频率。这些信息存储在nginx的共享内存中,意味着可以在nginx的worker进程间共享。name定义共享区域名称,size定义共享区域大小,1m可以存储16,000个IP,范例可以存储16,0000个IP。

    如果nginx需要添加新条目时存储空间耗尽,它将删除最旧的条目。如果释放的空间仍然不足以容纳新记录,则nginx返回503 (Service Temporarily Unavailable)状态码。除此之外,为防止内存耗尽,nginx每次创建新条目时,都会删除过去60秒钟内未使用过的两个条目。

    rate=rate:定义请求速率,范例中10r/s代表同一IP 1秒不能超过10个请求,实际上nginx以毫秒为单位跟踪请求,因此此限制相当于每100毫秒1个请求。因为我们没有突发事件配置,所以这意味着如果请求在前一个允许的请求之后不到100毫秒到达,则该请求将被拒绝。

  • limit_req_zone 设置的指令并不会直接生效,需要limit_reqhttp或者server或者location中使用配置,范例中limit_req在location上使用了mylimit配置,限制了在上一个请求发送后100ms接受下一个请求。

🍂 实际范例验证

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/m;
server {
    listen       80;
    server_name  192.168.124.128;
    location / {
       limit_req zone=mylimit;
       proxy_pass http://192.168.1.84:8080;
    }
}
  • rate=10r/m限定为每6秒接受一个请求,r/s以秒为单位限流,r/m以分钟为单位限流。
  • ⚡正常请求如下:
    [root@localhost opt]# curl http://192.168.124.128/
    hello, this is server
    
    超过速率请求,返回503 Service Temporarily Unavailable错误:
    [root@localhost opt]# curl http://192.168.124.128/
    <html>
    <head><title>503 Service Temporarily Unavailable</title></head>
        <body>
            <center>
                <h1>503 Service Temporarily Unavailable</h1>
             </center>
            <hr>
            <center>nginx/1.19.2</center>
          </body>
    </html>
    

3. 处理突发情况

🍂突发情况:rate=10r/s配置代表100毫秒只接受1个请求。若100毫秒收到2个请求,对于第二个请求,nignx将直接返回503给客户端。这并不是我们希望的,因为程序应用的请求往往不是线性的,而是突发性的。我们希望将多余的请求缓存下来,而不是粗暴的返回503错误。针对这种情况,我们在指令limit_req上使用burst参数,配置如下:👇

location /login/ {
    limit_req zone=mylimit burst=20;
    proxy_pass http://my_upstream;
}
  • burst参数,定义了超出rate请求速率,还可以接受多少请求放入到待处理的缓存队列中(示例mylimit,速率限制为每秒10个请求,或每100毫秒1个请求,若100毫秒内,接收了2个以上的请求,nginx会将多余的请求放入到缓存队列中)。
  • ⚡对于本例配置,若nginx同一时间接收到指定IP的21个请求,第一个请求立即转发到上游服务器组,并将其余20个请求放入队列。然后,每100毫秒转发一个排队的请求,当排队的请求超过20个的时候,将返回503错误。

🍂 实际范例验证

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=6r/m;
server {
    listen       80;
    server_name  192.168.124.128;
    location / {
       limit_req zone=mylimit burst=3;
       proxy_pass http://192.168.1.84:8080;
    }
}
  • ⚡代表每10秒转发一个请求,最多缓存3个请求。意味着,同一IP,同一时间发送5个请求,第五个请求将会被拒绝,第一个请求被转发到上游服务,队列中排队3个,最快40秒全部处理完成。

4. 无延迟排队

🍂无延迟排队实现了如下功能,同一IP指定时间段内接收并转发固定数量的请求,超量请求将会被拒接。
🍂nodelay配置:burst可以增加接纳请求的容量,但是并不实用,因为这会使我们的网站显得很慢。在示例中rate=10r/s,队列中第20个请求在2秒后才会被转发,这时客户端已经等待2秒了。若要解决这个情况,我们使用nodelay参数,配合burst参数使用:

location /login/ {
    limit_req zone=mylimit burst=20 nodelay;
    proxy_pass http://my_upstream;
}
  • ⚡在limit_req指令中配置了nodelay参数,nginx依旧会遵循rate配置的速率限制,但是不会通过间隔排队转发来实现。相反,当请求“过早”到达时,只要队列中有可用的插槽,nginx就会立即转发该请求。并将该插槽标记为“已占用”,直到经过适当的时间(在我们的示例中为100毫秒rate=10r/s)后,该插槽才会释放给其他请求使用。
  • burst=20 nodelay配置代表排队队列有20个插槽为空,当有21个请求同时从给定IP地址到达。nginx立即转发所有21个请求,并将队列中的20个插槽标记为已占用,然后每100毫秒释放1个插槽。(如果有25个请求,nginx将立即转发其中的21个请求,并将20个插槽标记为已占用,并拒绝最后4个请求返回503)
  • ⚡现在假设在转发第一组21个请求后101毫秒,同时有20个请求同时到达。队列中只有1个插槽被释放,因此nginx转发1个请求,并拒绝其他19个的请求返回503状态码。相反,如果在20个新请求到达之前已经过去501毫秒,则5个插槽是空闲的,因此nginx立即转发5个请求并拒绝15个。
  • ⚡整体效果相当于每秒10个请求的速率限制。nodelay将不限制请求间隔,则此选项很有用。

🍂 实际范例验证

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=6r/m;
server {
    listen       80;
    server_name  192.168.124.128;
    location / {
       limit_req zone=mylimit burst=3 nodelay;
       proxy_pass http://192.168.1.84:8080;
    }
}
  • ⚡上述配置相当于40秒,处理4个请求(10秒一个请求处理,3个队列等待,一个直接转发,(3+1)*10,若burst设置为20,则为(20+1)*10=210秒处理21个请求),每10秒释放一个队列插槽,接收一个外部请求。如果没有nodelay参数,从第二个请求开始客户端就开始漫长的等待返回。

对于大多数部署,我们都应当在limit_req指令中包含burstnodelay参数

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值