149人参与 • 2024-09-08 • Nginx
nginx 限流是一种用于控制并发连接数或请求速率的机制,旨在保护服务器免受过多的请求影响,防止因请求过载而导致系统性能下降或崩溃。对 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能够有效地控制请求的流量,防止过多的请求影响系统的稳定性。这对于保护服务器免受突发大流量的冲击是非常有用的。
nginx 限流需要使用 ngx_http_limit_req_module
模块实现
“流量限制”配置两个主要的指令,limit_req_zone
和 limit_req
:
limit_req_zone
指令设置创建共享内存区,用于存储限制请求的相关状态,但是它实际上并不限制请求速率的配置
拓展:共享内存区域的作用
limit_req_zone
指令定义的共享内存区域用于存储客户端请求的限制信息,例如请求的时间戳、请求计数器等。多个nginx worker进程可以同时访问这个共享内存区域,这样就可以实现对客户端请求的全局限制。limit_req
指令用于启用前面定义的共享内存区,从而实际限制请求速率limit_req_zone
指令通常在http块中定义,它需要以下三个参数:
limit_req_zone $variable zone=name:size rate=rate;
$variable
为限制的键值,可以是ip地址、http头部等;
name
为共享内存区域的名字;size
为共享内存区域的大小;rate
为限制速率,即每秒钟处理的请求数量。ip | 作用 | 备注 |
---|---|---|
192.168.221.130 | nginx-proxy | 反向代理服务器 |
192.168.221.136 | nginx-backend | 后端服务器 |
192.168.221.138 | ab压测服务器 | 对反向代理服务器对压测 |
//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
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)
//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"
日志字段
//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" "-"
#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。
一般情况下,客户端超过配置的流量限制时,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 流量控制/限流内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论