前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除
一、流量限制
1、介绍
流量限制 (rate-limiting);可用来限制用户在给定时间内HTTP请求的数量。请求,可以是一个简单网站首页的GET请求,也可以是登录表单的 POST 请求。流量限制可以用作安全目的:
- 减慢暴力密码破解的速率。
- 通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御DDOS攻击。
- 更常见的情况:该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。
2、Nginx如何限流
Nginx的”流量限制”使用漏桶算法(leaky bucket algorithm),该算法在通讯和分组交换计算机网络中广泛使用,用以处理带宽有限时的突发情况。就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在请求处理方面,水代表来自客户端的请 求,水桶代表根据”先进先出调度算法”(FIFO)等待被处理的请求队列,桶底漏出的水代表离开缓冲区被服务器处理的请求,桶口溢出的水代表被丢弃和不被处理的请求
二、配置基本的限流
1、实验环境
主机名 | IP地址 | 角色 |
---|---|---|
centos | 10.0.0.2 | web服务器 |
centos-2 | 10.0.0.3 | 代理服务器 |
2、配置文件
“流量限制”配置两个主要的指令, limit_req_zone 和 limit_req ,如下所示:
#-------------------10.0.0.3代理服务器配置-------------------#
# 定义一个共享内存区域,用于存储限流状态
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
upstream myweb {
server 10.0.0.2:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit;
proxy_pass http://myweb;
# 发送到上游服务器的HTTP头
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# -------------------10.0.0.2配置---------------------------#
server {
listen 80;
server_name localhost;
location /login {
root /usr/share/nginx/html;
index index.html index.html;
}
}
limit_req_zone指令定义了流量限制相关的参数,而limit_req指令在出现的上下文中启用流量限制(示例中,对于”/login/”的所有请求)
limit_req_zone 指令通常在HTTP块中定义,使其可在多个上下文中使用,它需要以下三个参数:
参数 | 说明 |
---|---|
key | 定义应用限制的请求特性 |
zone | 定义用于存储每个IP地址状态以及被限制请求URL访问频率的共享内存区域 |
rate | 定义最大请求速率 |
当Nginx需要添加新条目时存储空间不足,将会删除旧条目。如果释放的空间仍不够容纳新记录,Nginx将会返回 503状态码(Service Temporarily Unavailable)另外,为了防止内存被耗尽,Nginx每次创建新条目时,最多删除两条60秒内未使用的条目。
limit_req_zone 指令设置流量限制和共享内存区域的参数,但实际上并不限制请求速率。所以需要通过添加limit_req 指令,将流量限制应用在特定的 location 或者 server 块。在上面示例中,我们对 /login/ 请求进行流量限制。现在每个IP地址被限制为每秒只能请求10次 /login/ ,更准确地说,在前一个请求的100毫秒内不能请求该URL
3、测试访问
当我刷新一次时,正常访问
当访问超过设置的次数时:(本例为方便测试,设置1秒内1次访问)
三、处理突发
如果我们在100毫秒内接收到2个请求,怎么办?对于第二个请求,Nginx将给客户端返回状态码503。这可能并不是我们想要的结果,因为应用本质上趋向于突发性。相反地,我们希望缓冲任何超额的请求,然后及时地处理它们。我们更新下配置,在 limit_req 中使用 burst 参数:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit burst=20;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
burst 参数定义了超出zone指定速率的情况下,客户端还能发起多少请求。上一个请求100毫秒内到达的请求将会被放入队列,我们将队列大小设置为20;
意味着,如果从一个给定IP地址发送21个请求,Nginx会立即将第一个请求发送到上游服务器群,然后将余下20个请求放在队列中。然后每100毫秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过20时,Nginx才会向客户端返回503
四、无延迟排队
配置 burst 参数将会使通讯更流畅,但是可能会不太实用,因为该配置会使站点看起来很慢。在上面的示例中,队列中的第20个包需要等待2秒才能被转发,此时返回给客户端的响应可能不再有用。要解决这个情况,可以在 burst 参数后添加 nodelay 参数:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
使用 nodelay 参数,Nginx仍将根据 burst 参数分配队列中的位置,并应用已配置的速率限制,而不是清理队列中等待转发的请求。相反地,当一个请求到达“太早”时,只要在队列中能分配位置,Nginx将立即转发这个请求。将队列中的该位置标记为”taken”(占据),并且不会被释放以供另一个请求使用,直到一段时间后才会被释放(在这个示例中是,100毫秒后)
如果希望不限制两个请求间允许间隔的情况下实施“流量限制”, nodelay 参数是很实用的;注意: 对于大部分部署,建议使用 burst 和 nodelay 参数来配置 limit_req 指令;
五、配置流量控制相关功能
1、配置日志记录
默认情况下,Nginx会在日志中记录由于流量限制而延迟或丢弃的请求,如下所示:
2023/02/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000
by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET /
HTTP/1.0", host: "nginx.com"
日志条目中包含的字段:
参数 | 说明 |
---|---|
limiting requests | 表明日志条目记录的是被“流量限制”请求 |
excess | 每毫秒超过对应“流量限制”配置的请求数量 |
zone | 定义实施“流量限制”的区域 |
client | 发起请求的客户端IP地址 |
server | 服务器IP地址或主机名 |
request | 客户端发起的实际HTTP请求 |
host | HTTP报头中host的值 |
默认情况下,Nginx以error级别来记录被拒绝的请求,如上所示(Nginx以较低级别记录延迟请求,一般是info级别);如果要更改Nginx的日志记录级别,需要使用limit_req_log_level指令。下面修改为warn:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
2、发送到客户端到错误代码
一般情况下,客户端超过配置的流量限制时,Nginx响应状态码为503(Service Temporarily Unavailable)。可以使用 limit_req_status 指令来设置为其它状态码(例如下面的404状态码):
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;
limit_req_status 404;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
致谢
在此,我要对所有为知识共享做出贡献的个人和机构表示最深切的感谢。同时也感谢每一位花时间阅读这篇文章的读者,如果文章中有任何错误,欢迎留言指正。
学习永无止境,让我们共同进步!!