Nginx限制访问配置实践

Posted by Kaka Blog on May 11, 2019

前言

我们经常会遇到这种情况,服务器流量异常,负载过大等等。对于大流量恶意的攻击访问,会带来带宽的浪费,服务器压力,影响业务,往往考虑对同一个ip的连接数,并发数进行限制。下面说说ngx_http_limit_conn_module 模块来实现该需求。该模块可以根据定义的键来限制每个键值的连接数,如同一个IP来源的连接数。并不是所有的连接都会被该模块计数,只有那些正在被处理的请求(这些请求的头信息已被完全读入)所在的连接才会被计数。

访问限制模块

limit_conn_zone

  • 语法:limit_conn_zone $variable zone=name:size;
  • 默认值:no
  • 使用字段:http
  • $variable定义键

指令描述会话状态存储区域,键的状态中保存了当前连接数。 会话的数目按照指定的变量来决定,它依赖于使用的变量大小和memory_max_size的值。

例子:

limit_conn_zone $binary_remote_addr zone=one:10m;

解释:

  • zone=one 表示设置了名为“one”的存储区,大小为10兆字节
  • $binary_remote_addr表示客户端的IP地址作为键
  • $binary_remote_addr变量的长度是固定的4字节,存储状态在32位平台中占用32字节或64字节,在64位平台中占用64字节。
  • 1M共享空间可以保存3.2万个32位的状态,1.6万个64位的状态。 如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。

limit_conn

  • 语法:limit_conn zone_name number
  • 默认值:none
  • 配置段:http, server, location

指定每个给定键值的最大同时连接数,当超过这个数字时被返回503 (Service Temporarily Unavailable)错误。

例子:

server {
  location / {
    limit_conn one 3;
  }
}

解释: 同一IP同一时间只允许有3个连接。

限制访问频率模块

limit_req_zone

  • 语法:limit_req_zone $session_variable zone=name:size rate=rate
  • 默认值:none
  • 上下文:http

命令解析:为session会话状态分配一个大小为size的内存存储区,限制了每秒(分、小时)只接受rate个IP的频率。

例子:

limit_req_zone $binary_remote_addr zone=allips:10m rate=5r/s;

解释:

  • zone=allips表示设置了名为“allips”的存储区,大小为10M。
  • rate=5r/s 的意思是允许1秒钟不超过5个请求

limit_ewq

  • 语法:limit_req zone=name burst=burst [nodelay]
  • 默认值:none
  • 使用字段:http、server、location

命令解析:该指令用于指定使用的内存存储区(zone)名称,以及最大的突发请求数(burst)。如果请求的速率超过了limit_req_zone指令中设置的速率,这些请求将被延迟处理,在这种情况下,请求获得服务不可用信息,返回503状态码。

例子:

limit_req zone=allips burst=10 nodelay;

解释:

  • burst=10 最大延迟请求数量不大于10,
  • nodelay 请求不需要被延迟,超过最大延迟请求数量的服务器会立刻返回503状态码。如果没有该字段会造成大量的tcp连接请求等待。

实际应用

如果作为代理服务器,我们需要限制每个用户的请求速度和链接数量,但是,由于一个页面有多个子资源,如果毫无选择的都进行限制,那就会出现很多不必要的麻烦,如:一个页面有40个子资源,那么如果想让一个页面完整的显示,就需要将请求速度和连接数都调整到40,以此达到不阻塞用户正常请求,而这个限制,对服务器性能影响很大,几百用户就能把一台nginx的处理性能拉下来。

所以我们需要制定哪些请求是需要进行限制的,如html页面;哪些是不需要限制的,如css、js、图片等,这样就需要通过配置对应的location进一步细化。

我们不对css、js、gif、png,jpg等进行连接限制,而对除此之外的链接进行限制。

http {
    ···
    limit_conn_zone $binary_remote_addr zone=one:10m;    
    limit_req_zone $binary_remote_addr zone=allips:10m rate=5r/s;
    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location ~ .*\.(gif|png|css|js|icon)$ {
           proxy_set_header Host $http_host;
           proxy_set_header X-Real_IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       }

       location ~* .*\.(jpeg|jpg|JPG)$ {
           proxy_set_header Host $http_host;
           proxy_set_header X-Real_IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       }

        location / {
            root   html;
            index  index.html index.htm;
            #limit
            limit_conn one 3;
            limit_req zone=allips burst=10 nodelay;
        }
    }
    ···
}

这个配置的含义就是指定一个IP地址只用同时存在一个连接,一个IP地址一秒钟只处理5个请求,最大的突发请求数可以到10,请求速率超过或者会话存储区慢都将返回503。

测试

使用ab进行测试:

ab -n 11 -c 1 http://192.168.241.134/

没有失败请求。

ab -n 12 -c 1 http://192.168.241.134/

出现一条失败请求。

参考