Nginx 流量控制/限流的具体实现示例
作者:TA548464
1. Nginx 限流介绍
Nginx 限流是一种用于控制并发连接数或请求速率的机制,旨在保护服务器免受过多的请求影响,防止因请求过载而导致系统性能下降或崩溃。对 Nginx 限流的介绍可能涉及以下几个关键点:
- 什么是限流: 限流是一种流量控制手段,用于限制单位时间内可以通过系统的请求数或连接数。这有助于防止系统超负荷运行,保持系统的稳定性和可用性。
- 为何需要限流: 在高并发的网络环境中,突然涌入的大量请求可能会超出服务器的处理能力,导致性能下降甚至崩溃。通过限流,可以平滑处理请求,防止服务器不堪重负。
- Nginx限流配置: 在 Nginx 中,限流通常通过 ngx_http_limit_req_module 模块来实现。该模块允许你定义请求的速率限制,以及对超出限制的请求进行处理的方式,例如返回503错误或进入排队等待。
- 限流算法: Nginx 默认使用令牌桶算法进行限流。该算法基于令牌桶的概念,每个令牌代表一个请求。系统以一定的速率往令牌桶中放入令牌,请求时需要从令牌桶中取出令牌,当令牌不足时进行限流处理。
- 监控和调优: 监控 Nginx 的限流情况,并在需要时进行调优。监控可以通过 Nginx 的日志或专业监控工具实现,而调优可能涉及到调整限流参数、合理设置令牌桶容量等。
2. Nginx 如何限流?
漏桶算法是一种经典的流量控制算法,它以固定的速率接收请求,并以固定的速率处理请求,超过容量的请求将被丢弃或排队等待。
在 Nginx 中,使用 ngx_http_limit_req_module
模块来实现流量限制,其中漏桶算法是默认的限流算法。
以下是漏桶算法在 Nginx 中的一般工作原理:
令牌桶初始化: 在配置中,使用 limit_req_zone
指令来定义一个共享内存区域,作为令牌桶的存储。这个令牌桶会以指定的速率生成令牌。
http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; # 其他配置... }
令牌生成: 令牌桶以每秒1个的速率生成令牌。每个令牌表示一个允许通过的请求。
请求处理: 当有请求到达时,Nginx 会尝试从令牌桶中获取一个令牌。如果成功获取到令牌,请求将被处理;否则,请求将被拒绝或进入排队等待,具体取决于配置。
server { location / { limit_req zone=mylimit burst=5; # 其他配置... } }
在这个例子中,burst=5
表示在令牌桶为空时,允许瞬时突发的最大请求数为5。
漏桶补充: 每秒固定生成的令牌会不断补充令牌桶,但令牌桶的容量是有限的。如果请求过多,超过了令牌桶的容量,多余的请求将被丢弃或进行相应的处理。
通过使用漏桶算法,Nginx能够有效地控制请求的流量,防止过多的请求影响系统的稳定性。这对于保护服务器免受突发大流量的冲击是非常有用的。
3. Nginx 限流配置详解
Nginx 限流需要使用 ngx_http_limit_req_module
模块实现
“流量限制”配置两个主要的指令,limit_req_zone
和 limit_req
:
limit_req_zone
指令设置创建共享内存区,用于存储限制请求的相关状态,但是它实际上并不限制请求速率的配置
拓展:共享内存区域的作用
- 共享内存区域是一种可以被多个进程或线程同时访问的内存区域。
- 在 Nginx 中,
limit_req_zone
指令定义的共享内存区域用于存储客户端请求的限制信息,例如请求的时间戳、请求计数器等。多个nginx worker进程可以同时访问这个共享内存区域,这样就可以实现对客户端请求的全局限制。 - 共享内存区域的优点是:访问速度快、效率高,因为多个进程可以直接访问同一块内存, 避免了进程间通信的开销。
- 但是需要注意的是,共享内存区域的大小是有限制的,如果定义的区域过小,可能会导致无法存储足够的请求信息,从而影响限制的效果。因此,在使用共享内存区域时,需要根据实际情况选择合适的大小。
- 所以需要通过添加
limit_req
指令用于启用前面定义的共享内存区,从而实际限制请求速率 - 应用在特定的 location 或者 server 块。
limit_req_zone
指令通常在HTTP块中定义,它需要以下三个参数:
limit_req_zone $variable zone=name:size rate=rate;
$variable
为限制的键值,可以是IP地址、HTTP头部等;
name
为共享内存区域的名字;size
为共享内存区域的大小;rate
为限制速率,即每秒钟处理的请求数量。
4. Nginx 限流实验1
4.1. 环境准备
IP | 作用 | 备注 |
---|---|---|
192.168.221.130 | nginx-proxy | 反向代理服务器 |
192.168.221.136 | nginx-backend | 后端服务器 |
192.168.221.138 | ab压测服务器 | 对反向代理服务器对压测 |
4.2. 后端服务器配置
//192.168.221.136配置: [root@localhost conf.d]# vim login.conf server { listen 80; server_name www.Jltlogin.com; location /login { root /usr/share/nginx/html; index index.html index.html; } } //配置html文件 [root@localhost ~]# vim /usr/share/nginx/html/login/index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录页面</title> <style> body { background-color: #f2f2f2; font-family: Arial; } .login-box { width: 300px; padding: 20px; margin: 100px auto; background-color: #fff; border-radius: 10px; } h1 { text-align: center; } input[type=text], input[type=password] { width: 100%; padding: 12px 20px; margin: 8px 0; box-sizing: border-box; border: 2px solid #ccc; border-radius: 4px; } button[type=submit] { width: 100%; background-color: #4CAF50; color: white; padding: 14px 20px; margin: 8px 0; border: none; border-radius: 4px; cursor: pointer; } button[type=submit]:hover { background-color: #45a049; } </style> </head> <body> <div class="login-box"> <h1>登录</h1> <form> <label for="username">用户名:</label> <input type="text" id="username" name="username"><br><br> <label for="password">密码:</label> <input type="password" id="password" name="password"><br><br> <button type="submit">登录</button> </form> </div> </body> </html> [root@localhost login]# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful [root@localhost login]# systemctl restart nginx
本地host文件做好解析(windows中)
192.168.221.136 www.Jltlogin.com
浏览器访问测试,保证源站访问正常
http://www.Jltlogin.com/login
4.3. 反向代理服务器配置
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
Key - 定义应用限制的请求特性。示例中的 Nginx 变量$binary_remote_addr
,保存客户端IP地址的二进制形式。
Zone - 定义用于存储每个IP地址状态以及被限制请求URL访问频率的内存区域。通过zone=mylimit
标识区域的名字(自定义),冒号后面是区域大小。16000个IP地址的状态信息,大约需要1MB。
Rate - 连接请求。在该例子中,速率不能超过每秒1个请求。
#192.168.221.130 [root@localhost ~]$ cd /etc/nginx/conf.d/ [root@localhost conf.d]$ vim limit.conf limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; upstream myweb { server 192.168.221.136:80 weight=1 max_fails=1 fail_timeout=1; } server { listen 80; server_name www.Jltlogin-proxy.com; location /login { limit_req zone=mylimit; 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; } } #不使用upstream #limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; # server { # listen 80; # server_name www.Jltlogin-proxy.com; # location /login { # limit_req zone=mylimit; # proxy_pass http://www.Jltlogin.com; # 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; # } #} #尝试主配置文件http中修改添加,隐藏nginx版本 vim /etc/nginx/nginx.conf #在http中添加 server_tokens off;
配置本地host文件(138中,压力测试)
192.168.221.136 www.Jltlogin.com
配置本地host文件(windows中)
192.168.221.130 www.Jltlogin-proxy.com
浏览器访问测试
http://www.Jltlogin-proxy.com/login/
一秒一次/一秒钟多次访问(一秒多次按Ctrl+F5)
4.4. 对反向代理服务器进行压力测试
//192.168.221.138安装压力测试工具 [root@localhost ~]# yum install httpd-tools //添加hosts解析 [root@localhost ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.221.130 www.Jltlogin-proxy.com [root@localhost ~]# curl -I www.Jltlogin-proxy.com/login HTTP/1.1 301 Moved Permanently Server: nginx/1.24.0 Date: Sat, 29 Jul 2023 09:51:58 GMT Content-Type: text/html Content-Length: 169 Connection: keep-alive Location: http://www.Jltlogin-proxy.com/login/ [root@localhost ~]# ab -n1000 -c2 http://www.Jltlogin-proxy.com/login -n 请求数 -c 并发数 //130代理机器看错误日志: [root@localhost nginx]# tailf /var/log/nginx/error.log 2023/07/29 17:55:28 [error] 3996#3996: *1053 limiting requests, excess: 0.112 by zone "mylimit", client: 192.168.221.136, server: www.Jltlogin-proxy.com, request: "GET /login HTTP/1.0", host: "www.Jltlogin-proxy.com"
日志字段
- limiting requests - 表明日志条目记录的是被“流量限制”请求
- excess - 每毫秒超过对应“流量限制”配置的请求数量
- zone - 定义实施“流量限制”的区域
- client - 发起请求的客户端IP地址
- server - 服务器IP地址或主机名
- request - 客户端发起的实际HTTP请求
- host - HTTP报头中host的值
//130查看访问日志出现503 [root@localhost nginx]# tail -f /var/log/nginx/access.log 192.168.221.136 - - [29/Jul/2023:17:55:28 +0800] "GET /login HTTP/1.0" 503 197 "-" "ApacheBench/2.3" "-"
5. Nginx 限流实验2
#192.168.221.130反向代理服务器操作 [root@localhost conf.d]$ cp limit.conf{,.bak} [root@localhost conf.d]$ vim limit.conf limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; upstream myweb { server 192.168.221.136:80 weight=1 max_fails=1 fail_timeout=1; } server { listen 80; server_name www.Jltlogin-proxy.com; location /login { limit_req zone=mylimit burst=5; #limit_req zone=mylimit burst=5 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; } }
burst=5
表示最大延迟请求数量不大于5。超出的请求返回503状态码。limit_req zone=mylimit burst=5;
这意味着在任何给定的一秒钟内,只有 5 个请求会被允许通过。如果超过了这个限制,请求将会被暂时延迟,直到可以被处理为止。
limit_req zone=mylimit burst=5 nodelay;
该指令与上一个指令非常相似,但是添加了 nodelay
参数。
这个参数的作用是在达到限速阈值时不会延迟请求的处理。也就是说,如果超过了限速阈值,请求将不会被延迟,而是立即被处理。这可能会对服务器的性能产生【负面】影响,因为服务器需要处理更多的请求。但是,这样做可以提高用户体验,因为用户不需要等待请求被处理。
//192.168.221.138压力测试服务器操作: [root@localhost ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 //添加host解析 192.168.221.130 www.Jltlogin-proxy.com //开始做压力测试 [root@localhost ~]# ab -n1000 -c50 http://www.Jltlogin-proxy.com/login //192.168.221.130 反向代理机器上面看日志 [root@localhost ~]# tail -f /var/log/nginx/access.log 192.168.221.130 - - [29/Jul/2023:21:08:23 +0800] "GET /login HTTP/1.0" 301 169 "-" "ApacheBench/2.3" "-" 192.168.221.130 - - [29/Jul/2023:21:08:23 +0800] "GET /login HTTP/1.0" 503 197 "-" "ApacheBench/2.3" "-"
#nodelay:不延迟转发请求。 ……… location /login { #limit_req zone=mylimit burst=5; limit_req zone=mylimit burst=5 nodelay; #delay:延迟 proxy_pass http://myweb; …………
[root@localhost conf.d]# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful [root@localhost conf.d]# systemctl restart nginx //138继续进行压力测试 [root@localhost ~]# ab -n1000 -c50 http://www.Jltlogin-proxy.com/login //nodelay会使处理请求的速度变得要快很多,一下就处理完了 //192.168.221.130 反向代理机器上面看日志 [root@localhost ~]# tail -f /var/log/nginx/access.log 192.168.221.138 - - [29/Jul/2023:21:12:24 +0800] "GET /login HTTP/1.0" 301 169 "-" "ApacheBench/2.3" "-" 192.168.221.138 - - [29/Jul/2023:21:12:24 +0800] "GET /login HTTP/1.0" 503 197 "-" "ApacheBench/2.3" "-"
总结:
如果不加nodelay只有burst的时候,只会延迟转发请求超过限制的请求出现503错误
举例来说,burst=5,那么1秒内收到7个请求,会先处理前5个,第6和第7个请求会被推迟到下一秒处理,如果接下来很长时间依然超过5个请求,第6和第7个请求最后会收到503错误。
如果nodelay和burst参数都有,则不会延迟转发请求,并且超出规定的请求次数会返回503
可以理解为nodelay确保所有请求都得到及时处理,但不会改变burst的限制效果,超限的请求仍会是503。
6. 自定义返回错误代码
一般情况下,客户端超过配置的流量限制时,Nginx 响应状态码为 503(Service Temporarily Unavailable)。
我们可以使用 limit_req_status
指令来设置为其它状态码(例如下面的404状态码)
//130反向代理服务器操作: [root@localhost conf.d]# vim limit.conf limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; upstream myweb { server 192.168.221.136:80 weight=1 max_fails=1 fail_timeout=1; } server { listen 80; server_name www.Jltlogin-proxy.com; location /login { limit_req zone=mylimit; limit_req_status 404; #自定义限流的错误代码为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; } } [root@localhost conf.d]# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful [root@localhost conf.d]# systemctl restart nginx //138进行压力测试: [root@nginx-yum ~]# ab -n10 -c5 http://www.Jltlogin-proxy.com/login //130反向代理服务器查看日志 [root@localhost conf.d]# tailf /var/log/nginx/access.log 192.168.221.138 - - [29/Jul/2023:21:17:33 +0800] "GET /login HTTP/1.0" 301 169 "-" "ApacheBench/2.3" "-" 192.168.221.138 - - [29/Jul/2023:21:17:33 +0800] "GET /login HTTP/1.0" 404 153 "-" "ApacheBench/2.3" "-" //404 153:状态码、返回的数据长度,单位通常是字节(byte)
到此这篇关于Nginx 流量控制/限流的具体实现示例的文章就介绍到这了,更多相关Nginx 流量控制/限流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!