聊聊高并發(fā)系統(tǒng)之限流特技-2

摘要:上一篇《聊聊高并發(fā)系統(tǒng)限流特技-1》講了限流算法尖奔、應(yīng)用級(jí)限流毫蚓、分布式限流;本篇將介紹接入層限流實(shí)現(xiàn)蹲姐。

接入層限流

接入層通常指請(qǐng)求流量的入口磨取,該層的主要目的有:負(fù)載均衡、非法請(qǐng)求過(guò)濾柴墩、請(qǐng)求聚合忙厌、緩存、降級(jí)江咳、限流逢净、A/B測(cè)試、服務(wù)質(zhì)量監(jiān)控等等歼指,可以參考筆者寫(xiě)的《使用Nginx+Lua(OpenResty)開(kāi)發(fā)高性能Web應(yīng)用》爹土。

對(duì)于Nginx接入層限流可以使用Nginx自帶了兩個(gè)模塊:連接數(shù)限流模塊ngx_http_limit_conn_module和漏桶算法實(shí)現(xiàn)的請(qǐng)求限流模塊ngx_http_limit_req_module。還可以使用OpenResty提供的Lua限流模塊lua-resty-limit-traffic進(jìn)行更復(fù)雜的限流場(chǎng)景踩身。

limit_conn用來(lái)對(duì)某個(gè)KEY對(duì)應(yīng)的總的網(wǎng)絡(luò)連接數(shù)進(jìn)行限流胀茵,可以按照如IP、域名維度進(jìn)行限流挟阻。limit_req用來(lái)對(duì)某個(gè)KEY對(duì)應(yīng)的請(qǐng)求的平均速率進(jìn)行限流琼娘,并有兩種用法:平滑模式(delay)和允許突發(fā)模式(nodelay)峭弟。

ngx_http_limit_conn_module

limit_conn是對(duì)某個(gè)KEY對(duì)應(yīng)的總的網(wǎng)絡(luò)連接數(shù)進(jìn)行限流⊥哑矗可以按照IP來(lái)限制IP維度的總連接數(shù)瞒瘸,或者按照服務(wù)域名來(lái)限制某個(gè)域名的總連接數(shù)。但是記住不是每一個(gè)請(qǐng)求連接都會(huì)被計(jì)數(shù)器統(tǒng)計(jì)挪拟,只有那些被Nginx處理的且已經(jīng)讀取了整個(gè)請(qǐng)求頭的請(qǐng)求連接才會(huì)被計(jì)數(shù)器統(tǒng)計(jì)挨务。

配置示例:

================================

http {

limit_conn_zone$binary_remote_addr zone=addr:10m;

limit_conn_log_level error;

limit_conn_status 503;

...

server {

...

location /limit {

limit_conn addr 1;

}

================================

limit_conn:要配置存放KEY和計(jì)數(shù)器的共享內(nèi)存區(qū)域和指定KEY的最大連接數(shù);此處指定的最大連接數(shù)是1玉组,表示Nginx最多同時(shí)并發(fā)處理1個(gè)連接谎柄;

limit_conn_zone:用來(lái)配置限流KEY、及存放KEY對(duì)應(yīng)信息的共享內(nèi)存區(qū)域大泄喏ā朝巫;此處的KEY是“$binary_remote_addr”其表示IP地址,也可以使用如$server_name作為KEY來(lái)限制域名級(jí)別的最大連接數(shù)石景;

limit_conn_status:配置被限流后返回的狀態(tài)碼劈猿,默認(rèn)返回503;

limit_conn_log_level:配置記錄被限流后的日志級(jí)別潮孽,默認(rèn)error級(jí)別揪荣。

limit_conn的主要執(zhí)行過(guò)程如下所示:

1、請(qǐng)求進(jìn)入后首先判斷當(dāng)前l(fā)imit_conn_zone中相應(yīng)KEY的連接數(shù)是否超出了配置的最大連接數(shù)往史;

2.1仗颈、如果超過(guò)了配置的最大大小,則被限流椎例,返回limit_conn_status定義的錯(cuò)誤狀態(tài)碼挨决;

2.2、否則相應(yīng)KEY的連接數(shù)加1订歪,并注冊(cè)請(qǐng)求處理完成的回調(diào)函數(shù)脖祈;

3、進(jìn)行請(qǐng)求處理刷晋;

4盖高、在結(jié)束請(qǐng)求階段會(huì)調(diào)用注冊(cè)的回調(diào)函數(shù)對(duì)相應(yīng)KEY的連接數(shù)減1。

limt_conn可以限流某個(gè)KEY的總并發(fā)/請(qǐng)求數(shù)眼虱,KEY可以根據(jù)需要變化或舞。

按照IP限制并發(fā)連接數(shù)配置示例:

首先定義IP維度的限流區(qū)域:

================================

limit_conn_zone $binary_remote_addrzone=perip:10m;

================================

接著在要限流的location中添加限流邏輯:

================================

location /limit {

limit_conn perip 2;

echo "123";

}

================================

即允許每個(gè)IP最大并發(fā)連接數(shù)為2冻璃。

使用AB測(cè)試工具進(jìn)行測(cè)試械哟,并發(fā)數(shù)為5個(gè)嘱腥,總的請(qǐng)求數(shù)為5個(gè):

================================

ab -n 5 -c 5 http://localhost/limit

================================

將得到如下access.log輸出:

================================

[08/Jun/2016:20:10:51+0800] [1465373451.802] 200

[08/Jun/2016:20:10:51+0800] [1465373451.803] 200

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

================================

此處我們把a(bǔ)ccess log格式設(shè)置為log_format main? '[$time_local] [$msec] $status';分別是“日期 日期秒/毫秒值 響應(yīng)狀態(tài)碼”邮破。

如果被限流了诈豌,則在error.log中會(huì)看到類(lèi)似如下的內(nèi)容:

================================

2016/06/08 20:10:51 [error] 5662#0: *5limiting connections by zone "perip", client: 127.0.0.1, server: _,request: "GET /limit HTTP/1.0", host: "localhost"

================================

按照域名限制并發(fā)連接數(shù)配置示例:

首先定義域名維度的限流區(qū)域:

================================

limit_conn_zone $ server_name zone=perserver:10m;

================================

接著在要限流的location中添加限流邏輯:

================================

location /limit {

limit_conn perserver 2;

echo "123";

}

================================

即允許每個(gè)域名最大并發(fā)請(qǐng)求連接數(shù)為2;這樣配置可以實(shí)現(xiàn)服務(wù)器最大連接數(shù)限制抒和。

ngx_http_limit_req_module

limit_req是令牌桶算法實(shí)現(xiàn)矫渔,用于對(duì)指定KEY對(duì)應(yīng)的請(qǐng)求進(jìn)行限流,比如按照IP維度限制請(qǐng)求速率摧莽。

配置示例:

================================

http {

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

limit_conn_log_level error;

limit_conn_status 503;

...

server {

...

location /limit {

limit_req zone=one burst=5 nodelay;

}

================================

limit_req:配置限流區(qū)域庙洼、桶容量(突發(fā)容量,默認(rèn)0)镊辕、是否延遲模式(默認(rèn)延遲)油够;

limit_req_zone:配置限流KEY、及存放KEY對(duì)應(yīng)信息的共享內(nèi)存區(qū)域大小征懈、固定請(qǐng)求速率石咬;此處指定的KEY是“$binary_remote_addr”表示IP地址;固定請(qǐng)求速率使用rate參數(shù)配置卖哎,支持10r/s和60r/m鬼悠,即每秒10個(gè)請(qǐng)求和每分鐘60個(gè)請(qǐng)求,不過(guò)最終都會(huì)轉(zhuǎn)換為每秒的固定請(qǐng)求速率(10r/s為每100毫秒處理一個(gè)請(qǐng)求亏娜;60r/m焕窝,即每1000毫秒處理一個(gè)請(qǐng)求)。

limit_conn_status:配置被限流后返回的狀態(tài)碼维贺,默認(rèn)返回503它掂;

limit_conn_log_level:配置記錄被限流后的日志級(jí)別,默認(rèn)error級(jí)別幸缕。

limit_req的主要執(zhí)行過(guò)程如下所示:

1群发、請(qǐng)求進(jìn)入后首先判斷最后一次請(qǐng)求時(shí)間相對(duì)于當(dāng)前時(shí)間(第一次是0)是否需要限流,如果需要限流則執(zhí)行步驟2发乔,否則執(zhí)行步驟3熟妓;

2.1、如果沒(méi)有配置桶容量(burst)栏尚,則桶容量為0起愈;按照固定速率處理請(qǐng)求;如果請(qǐng)求被限流译仗,則直接返回相應(yīng)的錯(cuò)誤碼(默認(rèn)503)抬虽;

2.2、如果配置了桶容量(burst>0)且延遲模式(沒(méi)有配置nodelay)纵菌;如果桶滿(mǎn)了阐污,則新進(jìn)入的請(qǐng)求被限流;如果沒(méi)有滿(mǎn)則請(qǐng)求會(huì)以固定平均速率被處理(按照固定速率并根據(jù)需要延遲處理請(qǐng)求咱圆,延遲使用休眠實(shí)現(xiàn))笛辟;

2.3功氨、如果配置了桶容量(burst>0)且非延遲模式(配置了nodelay);不會(huì)按照固定速率處理請(qǐng)求手幢,而是允許突發(fā)處理請(qǐng)求捷凄;如果桶滿(mǎn)了,則請(qǐng)求被限流围来,直接返回相應(yīng)的錯(cuò)誤碼跺涤;

3、如果沒(méi)有被限流监透,則正常處理請(qǐng)求桶错;

4、Nginx會(huì)在相應(yīng)時(shí)機(jī)進(jìn)行選擇一些(3個(gè)節(jié)點(diǎn))限流KEY進(jìn)行過(guò)期處理才漆,進(jìn)行內(nèi)存回收牛曹。

場(chǎng)景2.1測(cè)試

首先定義IP維度的限流區(qū)域:

================================

limit_req_zone $binary_remote_addrzone=test:10m rate=500r/s;

================================

限制為每秒500個(gè)請(qǐng)求,固定平均速率為2毫秒一個(gè)請(qǐng)求醇滥。

接著在要限流的location中添加限流邏輯:

================================

location /limit {

limit_req zone=test;

echo "123";

}

================================

即桶容量為0(burst默認(rèn)為0)黎比,且延遲模式。

使用AB測(cè)試工具進(jìn)行測(cè)試鸳玩,并發(fā)數(shù)為2個(gè)阅虫,總的請(qǐng)求數(shù)為10個(gè):

================================

ab -n 10 -c 2 http://localhost/limit

================================

將得到如下access.log輸出:

================================

[08/Jun/2016:20:25:56+0800] [1465381556.410] 200

[08/Jun/2016:20:25:56 +0800][1465381556.410] 503

[08/Jun/2016:20:25:56 +0800][1465381556.411] 503

[08/Jun/2016:20:25:56+0800] [1465381556.411] 200

[08/Jun/2016:20:25:56 +0800][1465381556.412] 503

[08/Jun/2016:20:25:56 +0800][1465381556.412] 503

================================

雖然每秒允許500個(gè)請(qǐng)求,但是因?yàn)橥叭萘繛?不跟,所以流入的請(qǐng)求要么被處理要么被限流颓帝,無(wú)法延遲處理;另外平均速率在2毫秒左右窝革,比如1465381556.410和1465381556.411被處理了购城;有朋友會(huì)說(shuō)這固定平均速率不是1毫秒嘛,其實(shí)這是因?yàn)閷?shí)現(xiàn)算法沒(méi)那么精準(zhǔn)造成的虐译。

如果被限流在error.log中會(huì)看到如下內(nèi)容:

================================

2016/06/08 20:25:56 [error] 6130#0: *1962limiting requests, excess: 1.000 by zone "test", client: 127.0.0.1,server: _, request: "GET /limit HTTP/1.0", host:"localhost"

================================

如果被延遲了在error.log(日志級(jí)別要INFO級(jí)別)中會(huì)看到如下內(nèi)容:

================================

2016/06/10 09:05:23 [warn] 9766#0: *97021delaying request, excess: 0.368, by zone "test", client: 127.0.0.1,server: _, request: "GET /limit HTTP/1.0", host:"localhost"

================================

場(chǎng)景2.2測(cè)試

首先定義IP維度的限流區(qū)域:

================================

limit_req_zone $binary_remote_addr? ? zone=test:10m rate=2r/s;

================================

為了方便測(cè)試設(shè)置速率為每秒2個(gè)請(qǐng)求瘪板,即固定平均速率是500毫秒一個(gè)請(qǐng)求。

接著在要限流的location中添加限流邏輯:

================================

location /limit {

limit_req zone=test burst=3;

echo "123";

}

================================

固定平均速率為500毫秒一個(gè)請(qǐng)求漆诽,通容量為3侮攀,如果桶滿(mǎn)了新的請(qǐng)求被限流,否則可以進(jìn)入桶中排隊(duì)并等待(實(shí)現(xiàn)延遲模式)厢拭。

為了看出限流效果我們寫(xiě)了一個(gè)req.sh腳本:

================================

ab -c 6 -n 6 http://localhost/limit

sleep 0.3

ab -c 6 -n 6 http://localhost/limit

================================

首先進(jìn)行6個(gè)并發(fā)請(qǐng)求6次URL兰英,然后休眠300毫秒,然后再進(jìn)行6個(gè)并發(fā)請(qǐng)求6次URL供鸠;中間休眠目的是為了能跨越2秒看到效果畦贸,如果看不到如下的效果可以調(diào)節(jié)休眠時(shí)間。

將得到如下access.log輸出:

================================

[09/Jun/2016:08:46:43+0800] [1465433203.959] 200

[09/Jun/2016:08:46:43 +0800][1465433203.959] 503

[09/Jun/2016:08:46:43 +0800][1465433203.960] 503

[09/Jun/2016:08:46:44+0800] [1465433204.450] 200

[09/Jun/2016:08:46:44+0800] [1465433204.950] 200

[09/Jun/2016:08:46:45 +0800][1465433205.453] 200

[09/Jun/2016:08:46:45 +0800][1465433205.766] 503

[09/Jun/2016:08:46:45 +0800][1465433205.766] 503

[09/Jun/2016:08:46:45 +0800][1465433205.767] 503

[09/Jun/2016:08:46:45+0800] [1465433205.950] 200

[09/Jun/2016:08:46:46+0800] [1465433206.451] 200

[09/Jun/2016:08:46:46+0800] [1465433206.952] 200

================================

桶容量為3楞捂,即桶中在時(shí)間窗口內(nèi)最多流入3個(gè)請(qǐng)求薄坏,且按照2r/s的固定速率處理請(qǐng)求(即每隔500毫秒處理一個(gè)請(qǐng)求)正林;桶計(jì)算時(shí)間窗口(1.5秒)=速率(2r/s)/桶容量(3),也就是說(shuō)在這個(gè)時(shí)間窗口內(nèi)桶最多暫存3個(gè)請(qǐng)求颤殴。因此我們要以當(dāng)前時(shí)間往前推1.5秒和1秒來(lái)計(jì)算時(shí)間窗口內(nèi)的總請(qǐng)求數(shù);另外因?yàn)槟J(rèn)是延遲模式鼻忠,所以時(shí)間窗內(nèi)的請(qǐng)求要被暫存到桶中涵但,并以固定平均速率處理請(qǐng)求:

第一輪:有4個(gè)請(qǐng)求處理成功了,按照漏桶桶容量應(yīng)該最多3個(gè)才對(duì)帖蔓;這是因?yàn)橛?jì)算算法的問(wèn)題矮瘟,第一次計(jì)算因沒(méi)有參考值,所以第一次計(jì)算后塑娇,后續(xù)的計(jì)算才能有參考值澈侠,因此第一次成功可以忽略;這個(gè)問(wèn)題影響很小可以忽略埋酬;而且按照固定500毫秒的速率處理請(qǐng)求哨啃。

第二輪:因?yàn)榈谝惠喺?qǐng)求是突發(fā)來(lái)的,差不多都在1465433203.959時(shí)間點(diǎn)写妥,只是因?yàn)槁┩皩⑺俾蔬M(jìn)行了平滑變成了固定平均速率(每500毫秒一個(gè)請(qǐng)求)拳球;而第二輪計(jì)算時(shí)間應(yīng)基于1465433203.959;而第二輪突發(fā)請(qǐng)求差不多都在1465433205.766時(shí)間點(diǎn)珍特,因此計(jì)算桶容量的時(shí)間窗口應(yīng)基于1465433203.959和1465433205.766來(lái)計(jì)算祝峻,計(jì)算結(jié)果為1465433205.766這個(gè)時(shí)間點(diǎn)漏桶為空了,可以流入桶中3個(gè)請(qǐng)求扎筒,其他請(qǐng)求被拒絕莱找;又因?yàn)榈谝惠喿詈笠淮翁幚頃r(shí)間是1465433205.453,所以第二輪第一個(gè)請(qǐng)求被延遲到了1465433205.950嗜桌。這里也要注意固定平均速率只是在配置的速率左右奥溺,存在計(jì)算精度問(wèn)題,會(huì)有一些偏差症脂。

如果桶容量改為1(burst=1)谚赎,執(zhí)行req.sh腳本可以看到如下輸出:

================================

[09/Jun/2016:09:04:30+0800] [1465434270.362] 200

[09/Jun/2016:09:04:30 +0800][1465434270.371] 503

[09/Jun/2016:09:04:30 +0800] [1465434270.372]503

[09/Jun/2016:09:04:30 +0800][1465434270.372] 503

[09/Jun/2016:09:04:30 +0800][1465434270.372] 503

[09/Jun/2016:09:04:30+0800] [1465434270.864] 200

[09/Jun/2016:09:04:31 +0800][1465434271.178] 503

[09/Jun/2016:09:04:31 +0800][1465434271.178] 503

[09/Jun/2016:09:04:31 +0800][1465434271.178] 503

[09/Jun/2016:09:04:31 +0800][1465434271.178] 503

[09/Jun/2016:09:04:31 +0800][1465434271.179] 503

[09/Jun/2016:09:04:31+0800] [1465434271.366] 200

================================

桶容量為1,按照每1000毫秒一個(gè)請(qǐng)求的固定平均速率處理請(qǐng)求诱篷。

場(chǎng)景2.3測(cè)試

首先定義IP維度的限流區(qū)域:

================================

limit_req_zone $binary_remote_addrzone=test:10m rate=2r/s;

================================

為了方便測(cè)試配置為每秒2個(gè)請(qǐng)求壶唤,固定平均速率是500毫秒一個(gè)請(qǐng)求。

接著在要限流的location中添加限流邏輯:

================================

location /limit {

limit_req zone=test burst=3 nodelay;

echo "123";

}

================================

桶容量為3棕所,如果桶滿(mǎn)了直接拒絕新請(qǐng)求闸盔,且每秒2最多兩個(gè)請(qǐng)求,桶按照固定500毫秒的速率以nodelay模式處理請(qǐng)求琳省。

為了看到限流效果我們寫(xiě)了一個(gè)req.sh腳本:

================================

ab -c 6 -n 6 http://localhost/limit

sleep 1

ab -c 6 -n 6 http://localhost/limit

sleep 0.3

ab -c 6 -n 6 http://localhost/limit

sleep 0.3

ab -c 6 -n 6 http://localhost/limit

sleep 0.3

ab -c 6 -n 6 http://localhost/limit

sleep 2

ab -c 6 -n 6 http://localhost/limit

================================

將得到類(lèi)似如下access.log輸出:

================================

[09/Jun/2016:14:30:11+0800] [1465453811.754] 200

[09/Jun/2016:14:30:11+0800] [1465453811.755] 200

[09/Jun/2016:14:30:11+0800] [1465453811.755] 200

[09/Jun/2016:14:30:11+0800] [1465453811.759] 200

[09/Jun/2016:14:30:11 +0800][1465453811.759] 503

[09/Jun/2016:14:30:11 +0800][1465453811.759] 503

[09/Jun/2016:14:30:12+0800] [1465453812.776] 200

[09/Jun/2016:14:30:12+0800] [1465453812.776] 200

[09/Jun/2016:14:30:12 +0800][1465453812.776] 503

[09/Jun/2016:14:30:12 +0800][1465453812.777] 503

[09/Jun/2016:14:30:12 +0800][1465453812.777] 503

[09/Jun/2016:14:30:12 +0800][1465453812.777] 503

[09/Jun/2016:14:30:13 +0800] [1465453813.095]503

[09/Jun/2016:14:30:13 +0800][1465453813.097] 503

[09/Jun/2016:14:30:13 +0800][1465453813.097] 503

[09/Jun/2016:14:30:13 +0800][1465453813.097] 503

[09/Jun/2016:14:30:13 +0800][1465453813.097] 503

[09/Jun/2016:14:30:13 +0800][1465453813.098] 503

[09/Jun/2016:14:30:13+0800] [1465453813.425] 200

[09/Jun/2016:14:30:13 +0800][1465453813.425] 503

[09/Jun/2016:14:30:13 +0800][1465453813.425] 503

[09/Jun/2016:14:30:13 +0800][1465453813.426] 503

[09/Jun/2016:14:30:13 +0800][1465453813.426] 503

[09/Jun/2016:14:30:13 +0800][1465453813.426] 503

[09/Jun/2016:14:30:13+0800] [1465453813.754] 200

[09/Jun/2016:14:30:13 +0800][1465453813.755] 503

[09/Jun/2016:14:30:13 +0800][1465453813.755] 503

[09/Jun/2016:14:30:13 +0800][1465453813.756] 503

[09/Jun/2016:14:30:13 +0800][1465453813.756] 503

[09/Jun/2016:14:30:13 +0800][1465453813.756] 503

[09/Jun/2016:14:30:15+0800] [1465453815.278] 200

[09/Jun/2016:14:30:15+0800] [1465453815.278] 200

[09/Jun/2016:14:30:15+0800] [1465453815.278] 200

[09/Jun/2016:14:30:15 +0800][1465453815.278] 503

[09/Jun/2016:14:30:15 +0800][1465453815.279] 503

[09/Jun/2016:14:30:15 +0800][1465453815.279] 503

[09/Jun/2016:14:30:17+0800] [1465453817.300] 200

[09/Jun/2016:14:30:17+0800] [1465453817.300] 200

[09/Jun/2016:14:30:17+0800] [1465453817.300] 200

[09/Jun/2016:14:30:17+0800] [1465453817.301] 200

[09/Jun/2016:14:30:17 +0800][1465453817.301] 503

[09/Jun/2016:14:30:17 +0800][1465453817.301] 503

================================

桶容量為3(迎吵,即桶中在時(shí)間窗口內(nèi)最多流入3個(gè)請(qǐng)求躲撰,且按照2r/s的固定速率處理請(qǐng)求(即每隔500毫秒處理一個(gè)請(qǐng)求);桶計(jì)算時(shí)間窗口(1.5秒)=速率(2r/s)/桶容量(3)击费,也就是說(shuō)在這個(gè)時(shí)間窗口內(nèi)桶最多暫存3個(gè)請(qǐng)求拢蛋。因此我們要以當(dāng)前時(shí)間往前推1.5秒和1秒來(lái)計(jì)算時(shí)間窗口內(nèi)的總請(qǐng)求數(shù);另外因?yàn)榕渲昧薾odelay蔫巩,是非延遲模式谆棱,所以允許時(shí)間窗內(nèi)突發(fā)請(qǐng)求的;另外從本示例會(huì)看出兩個(gè)問(wèn)題:

第一輪和第七輪:有4個(gè)請(qǐng)求處理成功了圆仔;這是因?yàn)橛?jì)算算法的問(wèn)題垃瞧,本示例是如果2秒內(nèi)沒(méi)有請(qǐng)求,然后接著突然來(lái)了很多請(qǐng)求坪郭,第一次計(jì)算的結(jié)果將是不正確的个从;這個(gè)問(wèn)題影響很小可以忽略;

第五輪:1.0秒計(jì)算出來(lái)是3個(gè)請(qǐng)求歪沃;此處也是因計(jì)算精度的問(wèn)題嗦锐,也就是說(shuō)limit_req實(shí)現(xiàn)的算法不是非常精準(zhǔn)的,假設(shè)此處看成相對(duì)于2.75的話(huà)绸罗,1.0秒內(nèi)只有1次請(qǐng)求意推,所以還是允許1次請(qǐng)求的。

如果限流出錯(cuò)了珊蟀,可以配置錯(cuò)誤頁(yè)面:

================================

proxy_intercept_errors on;

recursive_error_pages on;

error_page 503 //www.jd.com/error.aspx;

================================

limit_conn_zone/limit_req_zone定義的內(nèi)存不足菊值,則后續(xù)的請(qǐng)求將一直被限流,所以需要根據(jù)需求設(shè)置好相應(yīng)的內(nèi)存大小育灸。

此處的限流都是單Nginx的腻窒,假設(shè)我們接入層有多個(gè)nginx,此處就存在和應(yīng)用級(jí)限流相同的問(wèn)題磅崭;那如何處理呢儿子?一種解決辦法:建立一個(gè)負(fù)載均衡層將按照限流KEY進(jìn)行一致性哈希算法將請(qǐng)求哈希到接入層Nginx上,從而相同KEY的將打到同一臺(tái)接入層Nginx上砸喻;另一種解決方案就是使用Nginx+Lua(OpenResty)調(diào)用分布式限流邏輯實(shí)現(xiàn)柔逼。

lua-resty-limit-traffic

之前介紹的兩個(gè)模塊使用上比較簡(jiǎn)單,指定KEY割岛、指定限流速率等就可以了愉适,如果我們想根據(jù)實(shí)際情況變化KEY、變化速率癣漆、變化桶大小等這種動(dòng)態(tài)特性维咸,使用標(biāo)準(zhǔn)模塊就很難去實(shí)現(xiàn)了,因此我們需要一種可編程來(lái)解決我們問(wèn)題;而OpenResty提供了lua限流模塊lua-resty-limit-traffic癌蓖,通過(guò)它可以按照更復(fù)雜的業(yè)務(wù)邏輯進(jìn)行動(dòng)態(tài)限流處理了瞬哼。其提供了limit.conn和limit.req實(shí)現(xiàn),算法與nginx limit_conn和limit_req是一樣的租副。

此處我們來(lái)實(shí)現(xiàn)ngx_http_limit_req_module中的【場(chǎng)景2.2測(cè)試】坐慰,不要忘記下載lua-resty-limit-traffic模塊并添加到OpenResty的lualib中。

配置用來(lái)存放限流用的共享字典:

================================

lua_shared_dict limit_req_store 100m;

================================

以下是實(shí)現(xiàn)【場(chǎng)景2.2測(cè)試】的限流代碼limit_req.lua:

================================

local limit_req = require "resty.limit.req"

local rate = 2 --固定平均速率 2r/s

local burst = 3? --桶容量

local error_status = 503

local nodelay = false --是否需要不延遲處理

local lim, err = limit_req.new("limit_req_store", rate, burst)

if not lim then --沒(méi)定義共享字典

ngx.exit(error_status)

end

local key = ngx.var.binary_remote_addr --IP維度的限流

--流入請(qǐng)求用僧,如果請(qǐng)求需要被延遲則delay > 0

local delay, err = lim:incoming(key, true)

if not delay and err == "rejected" then --超出桶大小了

ngx.exit(error_status)

end

if delay > 0 then? --根據(jù)需要決定是延遲或者不延遲處理

if nodelay then

--直接突發(fā)處理了

else

ngx.sleep(delay) --延遲處理

end

end

================================

即限流邏輯再nginx access階段被訪(fǎng)問(wèn)讨越,如果不被限流繼續(xù)后續(xù)流程;如果需要被限流要么sleep一段時(shí)間繼續(xù)后續(xù)流程永毅,要么返回相應(yīng)的狀態(tài)碼拒絕請(qǐng)求。

在分布式限流中我們使用了簡(jiǎn)單的Nginx+Lua進(jìn)行分布式限流人弓,有了這個(gè)模塊也可以使用這個(gè)模塊來(lái)實(shí)現(xiàn)分布式限流沼死。

另外在使用Nginx+Lua時(shí)也可以獲取ngx.var.connections_active進(jìn)行過(guò)載保護(hù),即如果當(dāng)前活躍連接數(shù)超過(guò)閾值進(jìn)行限流保護(hù)崔赌。

================================

if tonumber(ngx.var.connections_active) >= tonumber(limit) then

//限流

end

================================

nginx也提供了limit_rate用來(lái)對(duì)流量限速意蛀,如limit_rate 50k,表示限制下載速度為50k健芭。

到此筆者在工作中涉及的限流用法就介紹完县钥,這些算法中有些允許突發(fā),有些會(huì)整形為平滑慈迈,有些計(jì)算算法簡(jiǎn)單粗暴若贮;其中令牌桶算法和漏桶算法實(shí)現(xiàn)上是類(lèi)似的,只是表述的方向不太一樣痒留,對(duì)于業(yè)務(wù)來(lái)說(shuō)不必刻意去區(qū)分它們谴麦;因此需要根據(jù)實(shí)際場(chǎng)景來(lái)決定如何限流,最好的算法不一定是最適用的伸头。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匾效,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恤磷,更是在濱河造成了極大的恐慌面哼,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扫步,死亡現(xiàn)場(chǎng)離奇詭異魔策,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锌妻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)代乃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事搁吓≡” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵堕仔,是天一觀的道長(zhǎng)擂橘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)摩骨,這世上最難降的妖魔是什么通贞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮恼五,結(jié)果婚禮上昌罩,老公的妹妹穿的比我還像新娘。我一直安慰自己灾馒,他們只是感情好茎用,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著睬罗,像睡著了一般轨功。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上容达,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天古涧,我揣著相機(jī)與錄音,去河邊找鬼花盐。 笑死羡滑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的算芯。 我是一名探鬼主播啄栓,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼也祠!你這毒婦竟也來(lái)了昙楚?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诈嘿,失蹤者是張志新(化名)和其女友劉穎堪旧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奖亚,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淳梦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昔字。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爆袍。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡首繁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陨囊,到底是詐尸還是另有隱情弦疮,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布蜘醋,位于F島的核電站胁塞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏压语。R本人自食惡果不足惜啸罢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胎食。 院中可真熱鬧扰才,春花似錦、人聲如沸厕怜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酣倾。三九已至,卻和暖如春谤专,著一層夾襖步出監(jiān)牢的瞬間躁锡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工置侍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留映之,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓蜡坊,卻偏偏與公主長(zhǎng)得像杠输,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秕衙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載來(lái)自開(kāi)濤的聊聊高并發(fā)系統(tǒng)限流特技-2 上一篇《聊聊高并發(fā)系統(tǒng)限流特技-1》講了限流算法蠢甲、應(yīng)用級(jí)限流、分布式限流...
    meng_philip123閱讀 4,134評(píng)論 0 10
  • 接上文的聊聊高并發(fā)系統(tǒng)限流特技-1 原文來(lái)自開(kāi)濤的博客,找不到第此原文鏈接了 接入層限流 接入層通常指請(qǐng)求流量的入...
    望月成三人閱讀 3,971評(píng)論 0 4
  • 摘要:在開(kāi)發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來(lái)保護(hù)系統(tǒng):緩存据忘、降級(jí)和限流鹦牛。而有些場(chǎng)景并不能用緩存和降級(jí)來(lái)解決,因此需有一種...
    落羽成霜丶閱讀 2,152評(píng)論 0 18
  • 聊聊高并發(fā)系統(tǒng)限流特技-1來(lái)自開(kāi)濤的博客 在開(kāi)發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來(lái)保護(hù)系統(tǒng):緩存勇吊、降級(jí)和限流曼追。緩存的目的是...
    meng_philip123閱讀 6,644評(píng)論 1 20
  • 最近一直都在研究壓力測(cè)試客戶(hù)端的問(wèn)題,如果突破客戶(hù)端壓力測(cè)試線(xiàn)程汉规,端口等問(wèn)題礼殊,如果服務(wù)器端處理網(wǎng)絡(luò)請(qǐng)求處理不過(guò)來(lái),...
    望月成三人閱讀 8,651評(píng)論 1 25