Nginx 实现有效限流的深度解析
Nginx 提供了几种内置机制来实现不同层面的请求限速,以保护服务器资源,防止过度负载或 DDoS 攻击。以下是最常见的限流方法及其详细配置说明:
1. limit_req – 控制并发请求数量
limit_req 指令允许限制在特定时间内到达同一区域的请求数量,它可以应用于整个服务器,某个位置或者更细粒度上。
示例:
limit_req_zone $binary_remote_addr zone=my_limit:10m rate=1r/s;
location /popular {
limit_req zone=my_limit burst=5 nodelay;
}
在此配置中,“my_limit”是限流区的名字,存储在 10MB 内存中。请求被限制为每秒最多一次(rate=1r/s),并且允许瞬间暴发5个请求(burst=5),立即开始拒绝额外请求(nodelay)。
2. limit_conn – 控制每个来源的连接数
此指令限制单个 IP 地址的并发连接数,适用于防止单一来源的大量连接攻击。
limit_conn_zone $binary_remote_addr zone=my_conn:10m;
server {
...
limit_conn my_conn 5;
}
3. limit_req 与 Lua 模块结合使用
Nginx 的 Lua 模块提供了更灵活的限流策略。例如,你可以根据自定义算法动态计算每个用户的请求速率限制:
limit_req_zone $binary_remote_addr zone=my_limit:10m;
location /api {
set $limit_status "pass";
if ($request_uri ~ "^/api/") {
set_by_lua_block $req_per_minute {
ngx.req.read_body();
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(100)
if not red:connect('127.0.0.1', 6379) then
ngx.log(ngx.ERR, "failed to connect to Redis")
return nil
end
local key = string.format("%s:%d", ngx.var.remote_addr, ngx.time())
local val = red:get(key)
if val == nil then
red:set(key, 1, "EX", 60)
else
if tonumber(val) >= 100 then
ngx.log(ngx.INFO, "Too many requests from ", ngx.var.remote_addr)
ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
end
red:incrby(key, 1)
end
}
if $limit_status == "pass" then
limit_req my_limit;
end
}
}
在这个例子中,我们使用 Redis 来存储和追踪每个 IP 在过去一分钟内的请求次数。如果超过了设定的限制,则直接返回 429 Too Many Requests 错误。
4. 第三方模块
此外,还有像 limit_req_module、redis_upstream_health_check_module 等第三方模块提供更多高级功能。例如,redis_upstream_health_check_module 可以与 Redis 集群配合,实现跨机器间共享限流规则。
选择合适的限流策略
不同的场景下,你可能需要选择不同的限流机制。例如,limit_req 更适合于限制单个用户的访问速度,而 limit_conn 则能控制总体并发连接数。结合 Lua 动态计算可以满足更为复杂的业务逻辑,但同时也增加了配置的复杂性。
总之,在设计限流方案时,要考虑业务需求、安全考量和系统资源。合理的限流不仅能提高服务质量,还能有效防御网络攻击。