后端面试必备:使用Nginx实现限流算法与实现详解
Nginx面试题 -如何用Nginx做限流,有几种限流算法,分别如何实现?
回答重点
Nginx可以通过配置限制每个客户端请求的速率来实现限流。具体来说,Nginx有两种限流方式:基于请求速率限制和基于连接速率限制。
下面分别介绍这两种方式以及常用的限流算法。
1)基于请求速率限制:基于请求速率限制是指限制每个客户端的请求速率,常用的限流算法有以下几种:
- 漏桶算法:在单位时间内处理一定数量的请求,多余的请求则会放入一个“漏桶”中,随后以固定速率处理。
- 令牌桶算法:在每个单位时间内,将一定数量的“令牌”放入桶中,每次请求需要获取一个令牌才能被处理,当桶中没有令牌时,请求将被拒绝。
- 计数器算法:简单地对请求计数,并限制每个客户端在单位时间内最多可以处理的请求数量。
在Nginx中实现基于请求速率的限流通常需要使用模块,如ngxhttp_limit_req_module、ngx_http_limit_conn_module等。
- 基于连接速率限制:基于连接速率限制是指限制每个客户端的连接速率,常用的限流算法有以下几种:
- 并发连接数:限制每个客户端同时能够建立的连接数,以此来限制连接速率。
- 队列长度:将每个连接加入到一个队列中,限制队列中同时存在的连接数量,以此来限制连接速率。
在Nginx中实现基于连接速率的限流通常需要使用模块,如ngxhttp_limit_conn_module等。
要实现基于请求速率或连接速率的限流,我们需要在Nginx配置文件中设置相应的限流规则。以下是一个基于漏桶算法的Nginx配置示例:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location / {
limit_req zone=one burst=20;
proxy_pass http://backend;
}
}
}
其中,limitreq_zone指定了一个名为one的共享内存区域,用于存储请求的统计信息,同时指定了速率为10r/s。在location中,limitreq指定了限流规则,并将请求转发到后端服务器。
需要注意的是,不同的限流算法适用于不同的场景,我们需要根据实际情况选择合适的算法和配置参数。同时,在实际应用中,为了防止恶意攻击和DDOS攻击,通常需要将多种限流算法组合起来使用。
引言
在高并发Web应用中,限流是保护系统稳定性的重要手段。Nginx作为高性能的Web服务器和反向代理,提供了多种限流机制。本文将详细介绍Nginx中的限流技术,包括几种常见的限流算法及其实现方式。
一、为什么需要限流?
限流的主要目的是:
- 防止恶意攻击或爬虫耗尽服务器资源
- 避免突发流量导致服务不可用
- 保证核心业务的稳定性
- 实现服务的公平使用
二、Nginx限流基础
Nginx主要通过ngx_http_limit_req_module和ngx_http_limit_conn_module模块实现限流:
- limit_req_module:基于请求速率限流
- limit_conn_module:基于并发连接数限流
三、常见限流算法及Nginx实现
1. 漏桶算法 (Leaky Bucket)
算法原理:
请求以任意速率进入漏桶,漏桶以固定速率处理请求。当请求超过桶容量时,新的请求会被丢弃或排队。
Nginx实现:
使用limit_req_zone和limit_req指令:
http {
limit_req_zone $binary_remote_addr zone=leaky:10m rate=10r/s;
server {
location / {
limit_req zone=leaky burst=20 nodelay;
proxy_pass http://backend;
}
}
}
参数说明:
leaky: 共享内存区名称10m: 共享内存大小rate=10r/s: 每秒处理10个请求burst=20: 桶容量为20nodelay: 不延迟处理突发请求
2. 令牌桶算法 (Token Bucket)
算法原理:
系统以固定速率向桶中添加令牌,请求需要获取令牌才能被处理。当桶空时,新的请求会被拒绝。
Nginx实现:
Nginx没有原生支持令牌桶,但可以通过漏桶模拟:
http {
limit_req_zone $binary_remote_addr zone=token:10m rate=100r/s;
server {
location / {
limit_req zone=token burst=100;
proxy_pass http://backend;
}
}
}
这里设置一个高rate和相应的burst值来模拟令牌桶。
3. 固定窗口计数器 (Fixed Window)
算法原理:
将时间划分为固定窗口,每个窗口内限制请求数量。
Nginx实现:
使用limit_req的精确控制:
http {
limit_req_zone $binary_remote_addr zone=window:10m rate=100r/m;
server {
location / {
limit_req zone=window burst=100 nodelay;
proxy_pass http://backend;
}
}
}
4. 滑动窗口计数器 (Sliding Window)
算法原理:
统计最近时间窗口内的请求数,比固定窗口更精确。
Nginx实现:
Nginx没有原生支持,但可以通过Lua脚本或OpenResty实现:
http {
lua_shared_dict my_limit 10m;
server {
location / {
access_by_lua_block {
local limit = ngx.shared.my_limit
local key = ngx.var.binary_remote_addr
local now = ngx.now()
local window = 60 -- 60秒窗口
local newval, err = limit:incr(key..now, 1, 0, window)
if newval > 100 then
ngx.exit(503)
end
}
proxy_pass http://backend;
}
}
}
四、高级限流策略
1. 多维度限流
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;
limit_req_zone $server_name zone=per_server:10m rate=100r/s;
server {
location / {
limit_req zone=per_ip burst=20;
limit_req zone=per_server burst=100;
proxy_pass http://backend;
}
}
2. 白名单设置
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=protected:10m rate=5r/s;
server {
location / {
limit_req zone=protected burst=10 nodelay;
proxy_pass http://backend;
}
}
五、限流响应配置
自定义限流响应:
server {
location / {
limit_req zone=leaky burst=20 nodelay;
limit_req_status 429;
limit_req_log_level error;
error_page 429 /too_many_requests;
proxy_pass http://backend;
}
location = /too_many_requests {
return 429 '{"error": "Too many requests"}';
}
}
六、性能考虑
- 共享内存大小:根据限流键的数量合理设置
- 限流键选择:
$binary_remote_addr比$remote_addr更节省空间 - 日志级别:生产环境建议使用
error级别
七、总结
| 算法 | Nginx实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 漏桶 | limit_req | 平滑流量,控制精确 | 突发流量可能被严格限制 |
| 令牌桶 | limit_req模拟 | 允许一定突发 | 需要合理配置参数 |
| 固定窗口 | limit_req按时间设置 | 实现简单 | 窗口切换时可能突破限制 |
| 滑动窗口 | Lua脚本实现 | 限流精确 | 实现复杂,性能开销大 |
在实际应用中,应根据业务场景选择合适的限流策略,并通过监控和日志不断调整限流参数,以达到最佳的保护效果和用户体验。
更多推荐


所有评论(0)