Nginx 流量控制/限流的具體實(shí)現(xiàn)示例
1. Nginx 限流介紹
Nginx 限流是一種用于控制并發(fā)連接數(shù)或請(qǐng)求速率的機(jī)制,旨在保護(hù)服務(wù)器免受過(guò)多的請(qǐng)求影響,防止因請(qǐng)求過(guò)載而導(dǎo)致系統(tǒng)性能下降或崩潰。對(duì) Nginx 限流的介紹可能涉及以下幾個(gè)關(guān)鍵點(diǎn):
- 什么是限流: 限流是一種流量控制手段,用于限制單位時(shí)間內(nèi)可以通過(guò)系統(tǒng)的請(qǐng)求數(shù)或連接數(shù)。這有助于防止系統(tǒng)超負(fù)荷運(yùn)行,保持系統(tǒng)的穩(wěn)定性和可用性。
- 為何需要限流: 在高并發(fā)的網(wǎng)絡(luò)環(huán)境中,突然涌入的大量請(qǐng)求可能會(huì)超出服務(wù)器的處理能力,導(dǎo)致性能下降甚至崩潰。通過(guò)限流,可以平滑處理請(qǐng)求,防止服務(wù)器不堪重負(fù)。
- Nginx限流配置: 在 Nginx 中,限流通常通過(guò) ngx_http_limit_req_module 模塊來(lái)實(shí)現(xiàn)。該模塊允許你定義請(qǐng)求的速率限制,以及對(duì)超出限制的請(qǐng)求進(jìn)行處理的方式,例如返回503錯(cuò)誤或進(jìn)入排隊(duì)等待。
- 限流算法: Nginx 默認(rèn)使用令牌桶算法進(jìn)行限流。該算法基于令牌桶的概念,每個(gè)令牌代表一個(gè)請(qǐng)求。系統(tǒng)以一定的速率往令牌桶中放入令牌,請(qǐng)求時(shí)需要從令牌桶中取出令牌,當(dāng)令牌不足時(shí)進(jìn)行限流處理。
- 監(jiān)控和調(diào)優(yōu): 監(jiān)控 Nginx 的限流情況,并在需要時(shí)進(jìn)行調(diào)優(yōu)。監(jiān)控可以通過(guò) Nginx 的日志或?qū)I(yè)監(jiān)控工具實(shí)現(xiàn),而調(diào)優(yōu)可能涉及到調(diào)整限流參數(shù)、合理設(shè)置令牌桶容量等。
2. Nginx 如何限流?
漏桶算法是一種經(jīng)典的流量控制算法,它以固定的速率接收請(qǐng)求,并以固定的速率處理請(qǐng)求,超過(guò)容量的請(qǐng)求將被丟棄或排隊(duì)等待。
在 Nginx 中,使用 ngx_http_limit_req_module 模塊來(lái)實(shí)現(xiàn)流量限制,其中漏桶算法是默認(rèn)的限流算法。
以下是漏桶算法在 Nginx 中的一般工作原理:
令牌桶初始化: 在配置中,使用 limit_req_zone 指令來(lái)定義一個(gè)共享內(nèi)存區(qū)域,作為令牌桶的存儲(chǔ)。這個(gè)令牌桶會(huì)以指定的速率生成令牌。
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
# 其他配置...
}
令牌生成: 令牌桶以每秒1個(gè)的速率生成令牌。每個(gè)令牌表示一個(gè)允許通過(guò)的請(qǐng)求。
請(qǐng)求處理: 當(dāng)有請(qǐng)求到達(dá)時(shí),Nginx 會(huì)嘗試從令牌桶中獲取一個(gè)令牌。如果成功獲取到令牌,請(qǐng)求將被處理;否則,請(qǐng)求將被拒絕或進(jìn)入排隊(duì)等待,具體取決于配置。
server {
location / {
limit_req zone=mylimit burst=5;
# 其他配置...
}
}
在這個(gè)例子中,burst=5表示在令牌桶為空時(shí),允許瞬時(shí)突發(fā)的最大請(qǐng)求數(shù)為5。
漏桶補(bǔ)充: 每秒固定生成的令牌會(huì)不斷補(bǔ)充令牌桶,但令牌桶的容量是有限的。如果請(qǐng)求過(guò)多,超過(guò)了令牌桶的容量,多余的請(qǐng)求將被丟棄或進(jìn)行相應(yīng)的處理。
通過(guò)使用漏桶算法,Nginx能夠有效地控制請(qǐng)求的流量,防止過(guò)多的請(qǐng)求影響系統(tǒng)的穩(wěn)定性。這對(duì)于保護(hù)服務(wù)器免受突發(fā)大流量的沖擊是非常有用的。
3. Nginx 限流配置詳解
Nginx 限流需要使用 ngx_http_limit_req_module 模塊實(shí)現(xiàn)
“流量限制”配置兩個(gè)主要的指令,limit_req_zone 和 limit_req:
limit_req_zone 指令設(shè)置創(chuàng)建共享內(nèi)存區(qū),用于存儲(chǔ)限制請(qǐng)求的相關(guān)狀態(tài),但是它實(shí)際上并不限制請(qǐng)求速率的配置
拓展:共享內(nèi)存區(qū)域的作用
- 共享內(nèi)存區(qū)域是一種可以被多個(gè)進(jìn)程或線程同時(shí)訪問(wèn)的內(nèi)存區(qū)域。
- 在 Nginx 中,
limit_req_zone指令定義的共享內(nèi)存區(qū)域用于存儲(chǔ)客戶端請(qǐng)求的限制信息,例如請(qǐng)求的時(shí)間戳、請(qǐng)求計(jì)數(shù)器等。多個(gè)nginx worker進(jìn)程可以同時(shí)訪問(wèn)這個(gè)共享內(nèi)存區(qū)域,這樣就可以實(shí)現(xiàn)對(duì)客戶端請(qǐng)求的全局限制。 - 共享內(nèi)存區(qū)域的優(yōu)點(diǎn)是:訪問(wèn)速度快、效率高,因?yàn)槎鄠€(gè)進(jìn)程可以直接訪問(wèn)同一塊內(nèi)存, 避免了進(jìn)程間通信的開(kāi)銷。
- 但是需要注意的是,共享內(nèi)存區(qū)域的大小是有限制的,如果定義的區(qū)域過(guò)小,可能會(huì)導(dǎo)致無(wú)法存儲(chǔ)足夠的請(qǐng)求信息,從而影響限制的效果。因此,在使用共享內(nèi)存區(qū)域時(shí),需要根據(jù)實(shí)際情況選擇合適的大小。
- 所以需要通過(guò)添加
limit_req指令用于啟用前面定義的共享內(nèi)存區(qū),從而實(shí)際限制請(qǐng)求速率 - 應(yīng)用在特定的 location 或者 server 塊。
limit_req_zone 指令通常在HTTP塊中定義,它需要以下三個(gè)參數(shù):
limit_req_zone $variable zone=name:size rate=rate;$variable為限制的鍵值,可以是IP地址、HTTP頭部等;
name為共享內(nèi)存區(qū)域的名字;size為共享內(nèi)存區(qū)域的大小;rate為限制速率,即每秒鐘處理的請(qǐng)求數(shù)量。
4. Nginx 限流實(shí)驗(yàn)1
4.1. 環(huán)境準(zhǔn)備
| IP | 作用 | 備注 |
|---|---|---|
| 192.168.221.130 | nginx-proxy | 反向代理服務(wù)器 |
| 192.168.221.136 | nginx-backend | 后端服務(wù)器 |
| 192.168.221.138 | ab壓測(cè)服務(wù)器 | 對(duì)反向代理服務(wù)器對(duì)壓測(cè) |
4.2. 后端服務(wù)器配置
//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>登錄頁(yè)面</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
瀏覽器訪問(wèn)測(cè)試,保證源站訪問(wèn)正常
http://www.Jltlogin.com/login
4.3. 反向代理服務(wù)器配置
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;?Key - 定義應(yīng)用限制的請(qǐng)求特性。示例中的 Nginx 變量$binary_remote_addr,保存客戶端IP地址的二進(jìn)制形式。
?Zone - 定義用于存儲(chǔ)每個(gè)IP地址狀態(tài)以及被限制請(qǐng)求URL訪問(wèn)頻率的內(nèi)存區(qū)域。通過(guò)zone=mylimit 標(biāo)識(shí)區(qū)域的名字(自定義),冒號(hào)后面是區(qū)域大小。16000個(gè)IP地址的狀態(tài)信息,大約需要1MB。
?Rate - 連接請(qǐng)求。在該例子中,速率不能超過(guò)每秒1個(gè)請(qǐng)求。
#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中,壓力測(cè)試)
192.168.221.136 www.Jltlogin.com?
配置本地host文件(windows中)
192.168.221.130 www.Jltlogin-proxy.com
?瀏覽器訪問(wèn)測(cè)試
http://www.Jltlogin-proxy.com/login/
一秒一次/一秒鐘多次訪問(wèn)(一秒多次按Ctrl+F5)
4.4. 對(duì)反向代理服務(wù)器進(jìn)行壓力測(cè)試
//192.168.221.138安裝壓力測(cè)試工具 [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 請(qǐng)求數(shù) -c 并發(fā)數(shù) ? //130代理機(jī)器看錯(cuò)誤日志: [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 - 表明日志條目記錄的是被“流量限制”請(qǐng)求
- excess - 每毫秒超過(guò)對(duì)應(yīng)“流量限制”配置的請(qǐng)求數(shù)量
- zone - 定義實(shí)施“流量限制”的區(qū)域
- client - 發(fā)起請(qǐng)求的客戶端IP地址
- server - 服務(wù)器IP地址或主機(jī)名
- request - 客戶端發(fā)起的實(shí)際HTTP請(qǐng)求
- host - HTTP報(bào)頭中host的值
//130查看訪問(wèn)日志出現(xiàn)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 限流實(shí)驗(yàn)2
#192.168.221.130反向代理服務(wù)器操作
[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 表示最大延遲請(qǐng)求數(shù)量不大于5。超出的請(qǐng)求返回503狀態(tài)碼。limit_req zone=mylimit burst=5;這意味著在任何給定的一秒鐘內(nèi),只有 5 個(gè)請(qǐng)求會(huì)被允許通過(guò)。如果超過(guò)了這個(gè)限制,請(qǐng)求將會(huì)被暫時(shí)延遲,直到可以被處理為止。
?limit_req zone=mylimit burst=5 nodelay;該指令與上一個(gè)指令非常相似,但是添加了 nodelay 參數(shù)。
這個(gè)參數(shù)的作用是在達(dá)到限速閾值時(shí)不會(huì)延遲請(qǐng)求的處理。也就是說(shuō),如果超過(guò)了限速閾值,請(qǐng)求將不會(huì)被延遲,而是立即被處理。這可能會(huì)對(duì)服務(wù)器的性能產(chǎn)生【負(fù)面】影響,因?yàn)榉?wù)器需要處理更多的請(qǐng)求。但是,這樣做可以提高用戶體驗(yàn),因?yàn)橛脩舨恍枰却?qǐng)求被處理。
?//?192.168.221.138壓力測(cè)試服務(wù)器操作: [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 ? //開(kāi)始做壓力測(cè)試 [root@localhost ~]# ab -n1000 -c50 http://www.Jltlogin-proxy.com/login ? ? //192.168.221.130 反向代理機(jī)器上面看日志 [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:不延遲轉(zhuǎn)發(fā)請(qǐng)求。
………
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繼續(xù)進(jìn)行壓力測(cè)試 [root@localhost ~]# ab -n1000 -c50 http://www.Jltlogin-proxy.com/login //nodelay會(huì)使處理請(qǐng)求的速度變得要快很多,一下就處理完了 ? //192.168.221.130 反向代理機(jī)器上面看日志 [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" "-"
總結(jié):
如果不加nodelay只有burst的時(shí)候,只會(huì)延遲轉(zhuǎn)發(fā)請(qǐng)求超過(guò)限制的請(qǐng)求出現(xiàn)503錯(cuò)誤
舉例來(lái)說(shuō),burst=5,那么1秒內(nèi)收到7個(gè)請(qǐng)求,會(huì)先處理前5個(gè),第6和第7個(gè)請(qǐng)求會(huì)被推遲到下一秒處理,如果接下來(lái)很長(zhǎng)時(shí)間依然超過(guò)5個(gè)請(qǐng)求,第6和第7個(gè)請(qǐng)求最后會(huì)收到503錯(cuò)誤。
如果nodelay和burst參數(shù)都有,則不會(huì)延遲轉(zhuǎn)發(fā)請(qǐng)求,并且超出規(guī)定的請(qǐng)求次數(shù)會(huì)返回503
可以理解為nodelay確保所有請(qǐng)求都得到及時(shí)處理,但不會(huì)改變burst的限制效果,超限的請(qǐng)求仍會(huì)是503。
6. 自定義返回錯(cuò)誤代碼
一般情況下,客戶端超過(guò)配置的流量限制時(shí),Nginx 響應(yīng)狀態(tài)碼為 503(Service Temporarily Unavailable)。
我們可以使用 limit_req_status 指令來(lái)設(shè)置為其它狀態(tài)碼(例如下面的404狀態(tài)碼)
//130反向代理服務(wù)器操作:
[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; #自定義限流的錯(cuò)誤代碼為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進(jìn)行壓力測(cè)試:
[root@nginx-yum ~]# ab -n10 -c5 http://www.Jltlogin-proxy.com/login
?//130反向代理服務(wù)器查看日志
[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:狀態(tài)碼、返回的數(shù)據(jù)長(zhǎng)度,單位通常是字節(jié)(byte)到此這篇關(guān)于Nginx 流量控制/限流的具體實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Nginx 流量控制/限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Nginx設(shè)置HttpOnly Secure SameSite參數(shù)解決Cookie信息丟失
本文主要介紹了Nginx中Cookie缺少SameSite屬性的問(wèn)題,并詳細(xì)解釋了HttpOnly、Secure和SameSite屬性的作用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11
ubuntu上配置Nginx+PHP5 FastCGI服務(wù)器配置
ubuntu上配置Nginx+PHP5 FastCGI服務(wù)器配置方法, 需要的朋友可以參考下。2010-06-06
Nginx實(shí)現(xiàn)異步訪問(wèn)mysql的配置方法
這篇文章主要介紹了Nginx實(shí)現(xiàn)異步訪問(wèn)mysql的配置方法,本文先是講解了安裝配置方法,然后給出了使用方法,需要的朋友可以參考下2015-06-06
Nginx與Tomcat實(shí)現(xiàn)動(dòng)靜態(tài)分離和負(fù)載均衡
本篇文章主要介紹了Nginx與Tomcat實(shí)現(xiàn)動(dòng)靜態(tài)分離和負(fù)載均衡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-10-10
使用Nginx和內(nèi)網(wǎng)穿透實(shí)現(xiàn)多個(gè)本地Web站點(diǎn)的公網(wǎng)訪問(wèn)過(guò)程
本文介紹了如何通過(guò)Nginx配置文件的修改結(jié)合內(nèi)網(wǎng)穿透技術(shù),實(shí)現(xiàn)將多個(gè)本地Web站點(diǎn)暴露到公網(wǎng),具體步驟包括安裝和配置Nginx,選擇內(nèi)網(wǎng)穿透工具,配置Nginx服務(wù)器塊以及啟動(dòng)Nginx服務(wù)2026-01-01

