3. Nginx限速模块:从配置到实战

限速这事儿,说白了就是给流量装上水龙头。DDoS打过来的时候,你不可能让所有请求都通过——服务器扛不住。Nginx的限速模块,就是帮你拧紧这个水龙头的工具。

我个人习惯把限速分成两类:请求频率限制和并发连接限制。前者管的是“每秒能来多少次”,后者管的是“同时能有多少人在线”。两个模块配合使用,效果最好。

3.1 limit_req_module:请求频率限制

这个模块控制的是单位时间内的请求次数。比如,我限制每个IP每秒只能发10个请求,超过的就直接返回503。

先看基本配置:

http {
    # 定义限速区域
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

    server {
        location /api/ {
            limit_req zone=mylimit;
        }
    }
}

这里有几个关键点:

  • $binary_remote_addr:用客户端IP作为限速的key。用二进制格式比$remote_addr省内存。
  • zone=mylimit:10m:定义一个叫mylimit的共享内存区域,大小10MB。10MB大概能存16万个IP状态。
  • rate=10r/s:速率限制,每秒10个请求。
我的经验:rate的单位可以是r/s(每秒请求数)或r/m(每分钟请求数)。生产环境我一般用r/s,更精细。如果业务量特别大,比如API网关,我会用r/m来降低内存消耗。

但有个问题——上面的配置太死板。如果客户端在1秒内发了11个请求,第11个直接就被拒绝了。这在真实业务中不太合理。用户可能只是手抖了一下,多点了一次刷新。

这时候就需要burst参数了。

3.2 burst与nodelay:让限速更智能

burst,说白了就是个缓冲区。允许请求在短时间内超过限制,但超出的部分先排队,慢慢处理。

location /api/ {
    limit_req zone=mylimit burst=20;
}

这个配置的意思是:速率还是10r/s,但允许瞬间最多有20个请求排队。超过20个的,直接拒绝。

举个例子:

  • 第1秒来了30个请求 → 10个立即处理,20个进入burst队列,每秒处理10个
  • 第2秒又来了30个请求 → 此时burst队列里还有10个没处理完,新来的只能再排20个,剩下的10个被拒绝

但这样又有个问题——排队的请求会延迟响应。用户可能等得不耐烦,直接超时重试了。重试又会产生新请求,恶性循环。

所以有了nodelay参数:

location /api/ {
    limit_req zone=mylimit burst=20 nodelay;
}

nodelay的意思是:burst队列里的请求不延迟处理,只要队列没满,来了就立即处理。但速率限制依然生效——处理完这批突发请求后,后续请求必须按10r/s的速率来。

核心区别:
  • 不加nodelay:请求排队,延迟响应,但速率平滑
  • 加nodelay:请求立即响应,但速率可能瞬间飙高(不超过burst值)

我在项目中遇到过一个问题:加了nodelay之后,后端数据库压力突然增大。后来发现是因为burst设得太大,瞬间放进去太多请求。嗯,这里要注意——burst值要根据后端处理能力来设,不是越大越好。

3.3 limit_conn_module:并发连接限制

请求频率限制管的是“每秒来多少”,并发连接限制管的是“同时连多少”。这两个概念容易混淆,我简单解释一下:

  • 请求频率:1秒内发100个请求,但可能只用了1个连接(HTTP/2或Keep-Alive)
  • 并发连接:同时打开100个TCP连接,但每个连接可能只发1个请求

DDoS攻击经常是大量并发连接,每个连接发少量请求。这时候limit_req可能防不住,因为请求频率不高。但limit_conn能直接掐断连接数。

配置示例:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    server {
        location /download/ {
            limit_conn addr 10;
        }
    }
}

这个配置限制每个IP最多同时建立10个连接。超过的,Nginx直接返回503。

还可以限制总连接数:

http {
    limit_conn_zone $server_name zone=server:10m;

    server {
        limit_conn server 1000;  # 整个server最多1000个并发连接
    }
}
我曾经踩过的坑:limit_conn_zone的key如果用$server_name,要注意虚拟主机共享内存区域的问题。多个server用同一个zone,连接数会累加。我曾经因为没注意这个,导致一个低流量的站点被另一个高流量站点拖累,所有用户都连不上。后来改成每个server独立zone才解决。

3.4 两个模块的协同配置

实际生产中,我通常把两个模块一起用。一个管频率,一个管并发,互相补充。

一个典型的配置模板:

http {
    # 请求频率限制
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=30r/s;
    
    # 并发连接限制
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        listen 80;
        server_name api.example.com;

        location / {
            # 先检查并发连接数
            limit_conn conn_limit 20;
            
            # 再检查请求频率
            limit_req zone=req_limit burst=50 nodelay;
            
            # 错误响应
            limit_req_status 429;
            limit_conn_status 503;
            
            proxy_pass http://backend;
        }
    }
}

这个配置的效果:

  • 每个IP最多同时20个连接
  • 每秒最多30个请求,允许50个突发(立即处理)
  • 超限的返回429(Too Many Requests)或503(Service Unavailable)
我的建议:返回码最好区分开。429表示“你请求太快了”,503表示“服务器忙不过来”。这样排查问题时,看日志就知道是哪层限速触发了。

3.5 核心逻辑流程图

下面这张图展示了请求进入Nginx后的限速处理流程:

Nginx限速模块处理流程 客户端请求 第一步:limit_conn 并发连接检查 当前连接数是否超过限制? 返回 503 超过限制 未超过 第二步:limit_req 请求频率检查 当前速率是否超过 rate 限制? burst 队列是否已满? 有 nodelay 则立即处理 返回 429 队列满 队列未满 未超过 放行 → 后端服务 说明: 先检查并发连接数,再检查请求频率。burst队列配合nodelay可处理突发流量,但需注意后端承受能力。 超过并发限制返回503,超过请求频率且burst队列满返回429。

3.6 参数对比与选择建议

参数 作用 适用场景 注意事项
rate 基准速率,单位r/s或r/m 所有场景必配 根据后端处理能力设定,一般不超过后端TPS的80%
burst 突发缓冲区大小 允许短时间流量波动的场景 不宜过大,否则失去限速意义。建议不超过rate的5倍
nodelay 突发请求不延迟处理 对响应时间敏感的业务 配合burst使用,单独使用无效。注意后端压力
limit_conn 限制并发连接数 防止连接耗尽、慢速攻击 与limit_req互补,建议同时配置
我曾经犯过的错:有次给一个图片服务配限速,burst设了200,nodelay也加了。结果攻击流量一来,200个请求瞬间全部打到后端,直接把图片服务器打挂了。后来我把burst降到30,同时在后端也做了限流,才稳住。记住——Nginx限速只是第一道防线,后端也要有保护。

最后说一句:限速配置不是一劳永逸的。上线后要盯着监控看,根据实际流量调整参数。我一般先用宽松的配置跑一周,收集基线数据,再逐步收紧。这样既不影响正常用户,又能有效防御攻击。