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

摘要:在開發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來保護(hù)系統(tǒng):緩存、降級(jí)和限流。而有些場(chǎng)景并不能用緩存和降級(jí)來解決,因此需有一種手段來限制這些場(chǎng)景的并發(fā)/請(qǐng)求量等舔,即限流。本文就與大家分享一些關(guān)于高并發(fā)系統(tǒng)的限流特技糟趾。

在開發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來保護(hù)系統(tǒng):緩存慌植、降級(jí)和限流

緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)能處理的容量义郑,可謂是抗高并發(fā)流量的銀彈蝶柿;而降級(jí)是當(dāng)服務(wù)出問題或者影響到核心流程的性能則需要暫時(shí)屏蔽掉,待高峰或者問題解決后再打開非驮;而有些場(chǎng)景并不能用緩存和降級(jí)來解決交汤,比如稀缺資源(秒殺、搶購)劫笙、寫服務(wù)(如評(píng)論芙扎、下單)、頻繁的復(fù)雜查詢(評(píng)論的最后幾頁)填大,因此需有一種手段來限制這些場(chǎng)景的并發(fā)/請(qǐng)求量戒洼,即限流。

限流的目的是通過對(duì)并發(fā)訪問/請(qǐng)求進(jìn)行限速或者一個(gè)時(shí)間窗口內(nèi)的的請(qǐng)求進(jìn)行限速來保護(hù)系統(tǒng)允华,一旦達(dá)到限制速率則可以拒絕服務(wù)(定向到錯(cuò)誤頁或告知資源沒有了)圈浇、排隊(duì)或等待(比如秒殺寥掐、評(píng)論、下單)磷蜀、降級(jí)(返回兜底數(shù)據(jù)或默認(rèn)數(shù)據(jù)召耘,如商品詳情頁庫存默認(rèn)有貨)。

一般開發(fā)高并發(fā)系統(tǒng)常見的限流有:限制總并發(fā)數(shù)(比如數(shù)據(jù)庫連接池蠕搜、線程池)怎茫、限制瞬時(shí)并發(fā)數(shù)(如nginx的limit_conn模塊收壕,用來限制瞬時(shí)并發(fā)連接數(shù))妓灌、限制時(shí)間窗口內(nèi)的平均速率(如Guava的RateLimiter、nginx的limit_req模塊蜜宪,限制每秒的平均速率)虫埂;其他還有如限制遠(yuǎn)程接口調(diào)用速率、限制MQ的消費(fèi)速率圃验。另外還可以根據(jù)網(wǎng)絡(luò)連接數(shù)掉伏、網(wǎng)絡(luò)流量、CPU或內(nèi)存負(fù)載等來限流澳窑。

先有緩存這個(gè)銀彈斧散,后有限流來應(yīng)對(duì)618、雙十一高并發(fā)流量摊聋,在處理高并發(fā)問題上可以說是如虎添翼鸡捐,不用擔(dān)心瞬間流量導(dǎo)致系統(tǒng)掛掉或雪崩,最終做到有損服務(wù)而不是不服務(wù)麻裁;限流需要評(píng)估好箍镜,不可亂用,否則會(huì)正常流量出現(xiàn)一些奇怪的問題而導(dǎo)致用戶抱怨煎源。

在實(shí)際應(yīng)用時(shí)也不要太糾結(jié)算法問題色迂,因?yàn)橐恍┫蘖魉惴▽?shí)現(xiàn)是一樣的只是描述不一樣;具體使用哪種限流技術(shù)還是要根據(jù)實(shí)際場(chǎng)景來選擇手销,不要一味去找最佳模式歇僧,白貓黑貓能解決問題的就是好貓。

因在實(shí)際工作中遇到過許多人來問如何進(jìn)行限流锋拖,因此本文會(huì)詳細(xì)介紹各種限流手段馏慨。那么接下來我們從限流算法、應(yīng)用級(jí)限流姑隅、分布式限流写隶、接入層限流來詳細(xì)學(xué)習(xí)下限流技術(shù)手段。

限流算法

常見的限流算法有:令牌桶讲仰、漏桶慕趴。計(jì)數(shù)器也可以進(jìn)行粗暴限流實(shí)現(xiàn)。

令牌桶算法

令牌桶算法是一個(gè)存放固定容量令牌的桶,按照固定速率往桶里添加令牌冕房。令牌桶算法的描述如下:

假設(shè)限制2r/s躏啰,則按照500毫秒的固定速率往桶中添加令牌;

桶中最多存放b個(gè)令牌耙册,當(dāng)桶滿時(shí)给僵,新添加的令牌被丟棄或拒絕;

當(dāng)一個(gè)n個(gè)字節(jié)大小的數(shù)據(jù)包到達(dá)详拙,將從桶中刪除n個(gè)令牌帝际,接著數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)上;

如果桶中的令牌不足n個(gè)饶辙,則不會(huì)刪除令牌蹲诀,且該數(shù)據(jù)包將被限流(要么丟棄,要么緩沖區(qū)等待)弃揽。

漏桶算法

漏桶作為計(jì)量工具(The Leaky Bucket Algorithm as a Meter)時(shí)脯爪,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolicing),漏桶算法的描述如下:

一個(gè)固定容量的漏桶矿微,按照常量固定速率流出水滴痕慢;

如果桶是空的,則不需流出水滴涌矢;

可以以任意速率流入水滴到漏桶掖举;

如果流入水滴超出了桶的容量,則流入的水滴溢出了(被丟棄)蒿辙,而漏桶容量是不變的拇泛。

令牌桶和漏桶對(duì)比:

令牌桶是按照固定速率往桶中添加令牌,請(qǐng)求是否被處理需要看桶中令牌是否足夠思灌,當(dāng)令牌數(shù)減為零時(shí)則拒絕新的請(qǐng)求俺叭;

漏桶則是按照常量固定速率流出請(qǐng)求,流入請(qǐng)求速率任意泰偿,當(dāng)流入的請(qǐng)求數(shù)累積到漏桶容量時(shí)熄守,則新流入的請(qǐng)求被拒絕;

令牌桶限制的是平均流入速率(允許突發(fā)請(qǐng)求耗跛,只要有令牌就可以處理裕照,支持一次拿3個(gè)令牌,4個(gè)令牌)调塌,并允許一定程度突發(fā)流量晋南;

漏桶限制的是常量流出速率(即流出速率是一個(gè)固定常量值,比如都是1的速率流出羔砾,而不能一次是1负间,下次又是2)偶妖,從而平滑突發(fā)流入速率;

令牌桶允許一定程度的突發(fā)政溃,而漏桶主要目的是平滑流入速率趾访;

兩個(gè)算法實(shí)現(xiàn)可以一樣,但是方向是相反的董虱,對(duì)于相同的參數(shù)得到的限流效果是一樣的扼鞋。

另外有時(shí)候我們還使用計(jì)數(shù)器來進(jìn)行限流,主要用來限制總并發(fā)數(shù)愤诱,比如數(shù)據(jù)庫連接池云头、線程池、秒殺的并發(fā)數(shù)转锈;只要全局總請(qǐng)求數(shù)或者一定時(shí)間段的總請(qǐng)求數(shù)設(shè)定的閥值則進(jìn)行限流盘寡,是簡單粗暴的總數(shù)量限流楚殿,而不是平均速率限流撮慨。

到此基本的算法就介紹完了,接下來我們首先看看應(yīng)用級(jí)限流脆粥。

應(yīng)用級(jí)限流

限流總并發(fā)/連接/請(qǐng)求數(shù)

對(duì)于一個(gè)應(yīng)用系統(tǒng)來說一定會(huì)有極限并發(fā)/請(qǐng)求數(shù)砌溺,即總有一個(gè)TPS/QPS閥值,如果超了閥值則系統(tǒng)就會(huì)不響應(yīng)用戶請(qǐng)求或響應(yīng)的非常慢变隔,因此我們最好進(jìn)行過載保護(hù)规伐,防止大量請(qǐng)求涌入擊垮系統(tǒng)。

如果你使用過Tomcat匣缘,其Connector 其中一種配置有如下幾個(gè)參數(shù):

acceptCount:如果Tomcat的線程都忙于響應(yīng)猖闪,新來的連接會(huì)進(jìn)入隊(duì)列排隊(duì),如果超出排隊(duì)大小肌厨,則拒絕連接培慌;

maxConnections: 瞬時(shí)最大連接數(shù),超出的會(huì)排隊(duì)等待柑爸;

maxThreads:Tomcat能啟動(dòng)用來處理請(qǐng)求的最大線程數(shù)吵护,如果請(qǐng)求處理量一直遠(yuǎn)遠(yuǎn)大于最大線程數(shù)則可能會(huì)僵死。

詳細(xì)的配置請(qǐng)參考官方文檔表鳍。另外如Mysql(如max_connections)馅而、Redis(如tcp-backlog)都會(huì)有類似的限制連接數(shù)的配置。

限流總資源數(shù)

如果有的資源是稀缺資源(如數(shù)據(jù)庫連接譬圣、線程)瓮恭,而且可能有多個(gè)系統(tǒng)都會(huì)去使用它,那么需要限制應(yīng)用厘熟;可以使用池化技術(shù)來限制總資源數(shù):連接池屯蹦、線程池诸衔。比如分配給每個(gè)應(yīng)用的數(shù)據(jù)庫連接是100,那么本應(yīng)用最多可以使用100個(gè)資源颇玷,超出了可以等待或者拋異常笨农。

限流某個(gè)接口的總并發(fā)/請(qǐng)求數(shù)

如果接口可能會(huì)有突發(fā)訪問情況,但又擔(dān)心訪問量太大造成崩潰帖渠,如搶購業(yè)務(wù)谒亦;這個(gè)時(shí)候就需要限制這個(gè)接口的總并發(fā)/請(qǐng)求數(shù)總請(qǐng)求數(shù)了;因?yàn)榱6缺容^細(xì)空郊,可以為每個(gè)接口都設(shè)置相應(yīng)的閥值份招。可以使用Java中的AtomicLong進(jìn)行限流:

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

try {

if(atomic.incrementAndGet() > 限流數(shù)) {

//拒絕請(qǐng)求

}

//處理請(qǐng)求

} finally {

atomic.decrementAndGet();

}

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

適合對(duì)業(yè)務(wù)無損的服務(wù)或者需要過載保護(hù)的服務(wù)進(jìn)行限流狞甚,如搶購業(yè)務(wù)锁摔,超出了大小要么讓用戶排隊(duì),要么告訴用戶沒貨了哼审,對(duì)用戶來說是可以接受的谐腰。而一些開放平臺(tái)也會(huì)限制用戶調(diào)用某個(gè)接口的試用請(qǐng)求量,也可以用這種計(jì)數(shù)器方式實(shí)現(xiàn)涩盾。這種方式也是簡單粗暴的限流十气,沒有平滑處理,需要根據(jù)實(shí)際情況選擇使用春霍;

限流某個(gè)接口的時(shí)間窗請(qǐng)求數(shù)

即一個(gè)時(shí)間窗口內(nèi)的請(qǐng)求數(shù)砸西,如想限制某個(gè)接口/服務(wù)每秒/每分鐘/每天的請(qǐng)求數(shù)/調(diào)用量。如一些基礎(chǔ)服務(wù)會(huì)被很多其他系統(tǒng)調(diào)用址儒,比如商品詳情頁服務(wù)會(huì)調(diào)用基礎(chǔ)商品服務(wù)調(diào)用芹枷,但是怕因?yàn)楦铝勘容^大將基礎(chǔ)服務(wù)打掛,這時(shí)我們要對(duì)每秒/每分鐘的調(diào)用量進(jìn)行限速莲趣;一種實(shí)現(xiàn)方式如下所示:

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

LoadingCache counter =

CacheBuilder.newBuilder()

.expireAfterWrite(2, TimeUnit.SECONDS)

.build(new CacheLoader() {

@Override

public AtomicLong load(Long seconds) throws Exception {

return new AtomicLong(0);

}

});

long limit = 1000;

while(true) {

//得到當(dāng)前秒

long currentSeconds = System.currentTimeMillis() / 1000;

if(counter.get(currentSeconds).incrementAndGet() > limit) {

System.out.println("限流了:" + currentSeconds);

continue;

}

//業(yè)務(wù)處理

}

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

我們使用Guava的Cache來存儲(chǔ)計(jì)數(shù)器鸳慈,過期時(shí)間設(shè)置為2秒(保證1秒內(nèi)的計(jì)數(shù)器是有的),然后我們獲取當(dāng)前時(shí)間戳然后取秒數(shù)來作為KEY進(jìn)行計(jì)數(shù)統(tǒng)計(jì)和限流妖爷,這種方式也是簡單粗暴蝶涩,剛才說的場(chǎng)景夠用了。

平滑限流某個(gè)接口的請(qǐng)求數(shù)

之前的限流方式都不能很好地應(yīng)對(duì)突發(fā)請(qǐng)求絮识,即瞬間請(qǐng)求可能都被允許從而導(dǎo)致一些問題绿聘;因此在一些場(chǎng)景中需要對(duì)突發(fā)請(qǐng)求進(jìn)行整形,整形為平均速率請(qǐng)求處理(比如5r/s次舌,則每隔200毫秒處理一個(gè)請(qǐng)求熄攘,平滑了速率)。這個(gè)時(shí)候有兩種算法滿足我們的場(chǎng)景:令牌桶和漏桶算法彼念。Guava框架提供了令牌桶算法實(shí)現(xiàn)挪圾,可直接拿來使用浅萧。

Guava RateLimiter提供了令牌桶算法實(shí)現(xiàn):平滑突發(fā)限流(SmoothBursty)和平滑預(yù)熱限流(SmoothWarmingUp)實(shí)現(xiàn)。

SmoothBursty

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

RateLimiter limiter = RateLimiter.create(5);

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

將得到類似如下的輸出:

0.0

0.198239

0.196083

0.200609

0.199599

0.19961

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

1哲思、RateLimiter.create(5) 表示桶容量為5且每秒新增5個(gè)令牌洼畅,即每隔200毫秒新增一個(gè)令牌;

2棚赔、limiter.acquire()表示消費(fèi)一個(gè)令牌帝簇,如果當(dāng)前桶中有足夠令牌則成功(返回值為0),如果桶中沒有令牌則暫停一段時(shí)間靠益,比如發(fā)令牌間隔是200毫秒丧肴,則等待200毫秒后再去消費(fèi)令牌(如上測(cè)試用例返回的為0.198239,差不多等待了200毫秒桶中才有令牌可用)胧后,這種實(shí)現(xiàn)將突發(fā)請(qǐng)求速率平均為了固定請(qǐng)求速率芋浮。

再看一個(gè)突發(fā)示例:

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

RateLimiter limiter = RateLimiter.create(5);

System.out.println(limiter.acquire(5));

System.out.println(limiter.acquire(1));

System.out.println(limiter.acquire(1))

將得到類似如下的輸出:

0.0

0.98745

0.183553

0.199909

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

limiter.acquire(5)表示桶的容量為5且每秒新增5個(gè)令牌,令牌桶算法允許一定程度的突發(fā)壳快,所以可以一次性消費(fèi)5個(gè)令牌纸巷,但接下來的limiter.acquire(1)將等待差不多1秒桶中才能有令牌,且接下來的請(qǐng)求也整形為固定速率了濒憋。

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

RateLimiter limiter = RateLimiter.create(5);

System.out.println(limiter.acquire(10));

System.out.println(limiter.acquire(1));

System.out.println(limiter.acquire(1));

將得到類似如下的輸出:

0.0

1.997428

0.192273

0.200616

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

同上邊的例子類似何暇,第一秒突發(fā)了10個(gè)請(qǐng)求陶夜,令牌桶算法也允許了這種突發(fā)(允許消費(fèi)未來的令牌)凛驮,但接下來的limiter.acquire(1)將等待差不多2秒桶中才能有令牌,且接下來的請(qǐng)求也整形為固定速率了条辟。

接下來再看一個(gè)突發(fā)的例子:

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

RateLimiter limiter = RateLimiter.create(2);

System.out.println(limiter.acquire());

Thread.sleep(2000L);

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

System.out.println(limiter.acquire());

將得到類似如下的輸出:

0.0

0.0

0.0

0.0

0.499876

0.495799

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

1黔夭、創(chuàng)建了一個(gè)桶容量為2且每秒新增2個(gè)令牌;

2羽嫡、首先調(diào)用limiter.acquire()消費(fèi)一個(gè)令牌,此時(shí)令牌桶可以滿足(返回值為0);

3葛账、然后線程暫停2秒椿胯,接下來的兩個(gè)limiter.acquire()都能消費(fèi)到令牌,第三個(gè)limiter.acquire()也同樣消費(fèi)到了令牌魂爪,到第四個(gè)時(shí)就需要等待500毫秒了先舷。

此處可以看到我們?cè)O(shè)置的桶容量為2(即允許的突發(fā)量),這是因?yàn)镾moothBursty中有一個(gè)參數(shù):最大突發(fā)秒數(shù)(maxBurstSeconds)默認(rèn)值是1s滓侍,突發(fā)量/桶容量=速率*maxBurstSeconds蒋川,所以本示例桶容量/突發(fā)量為2,例子中前兩個(gè)是消費(fèi)了之前積攢的突發(fā)量撩笆,而第三個(gè)開始就是正常計(jì)算的了捺球。令牌桶算法允許將一段時(shí)間內(nèi)沒有消費(fèi)的令牌暫存到令牌桶中缸浦,留待未來使用,并允許未來請(qǐng)求的這種突發(fā)氮兵。

SmoothBursty通過平均速率和最后一次新增令牌的時(shí)間計(jì)算出下次新增令牌的時(shí)間的裂逐,另外需要一個(gè)桶暫存一段時(shí)間內(nèi)沒有使用的令牌(即可以突發(fā)的令牌數(shù))。另外RateLimiter還提供了tryAcquire方法來進(jìn)行無阻塞或可超時(shí)的令牌消費(fèi)泣栈。

因?yàn)镾moothBursty允許一定程度的突發(fā)絮姆,會(huì)有人擔(dān)心如果允許這種突發(fā),假設(shè)突然間來了很大的流量秩霍,那么系統(tǒng)很可能扛不住這種突發(fā)篙悯。因此需要一種平滑速率的限流工具,從而系統(tǒng)冷啟動(dòng)后慢慢的趨于平均固定速率(即剛開始速率小一些铃绒,然后慢慢趨于我們?cè)O(shè)置的固定速率)鸽照。Guava也提供了SmoothWarmingUp來實(shí)現(xiàn)這種需求,其可以認(rèn)為是漏桶算法颠悬,但是在某些特殊場(chǎng)景又不太一樣矮燎。

SmoothWarmingUp創(chuàng)建方式:RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit)

permitsPerSecond表示每秒新增的令牌數(shù),warmupPeriod表示在從冷啟動(dòng)速率過渡到平均速率的時(shí)間間隔赔癌。

示例如下:

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

RateLimiter limiter = RateLimiter.create(5, 1000, TimeUnit.MILLISECONDS);

for(int i = 1; i < 5;i++) {

System.out.println(limiter.acquire());

}

Thread.sleep(1000L);

for(int i = 1; i < 5;i++) {

System.out.println(limiter.acquire());

}

將得到類似如下的輸出:

0.0

0.51767

0.357814

0.219992

0.199984

0.0

0.360826

0.220166

0.199723

0.199555

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

速率是梯形上升速率的诞外,也就是說冷啟動(dòng)時(shí)會(huì)以一個(gè)比較大的速率慢慢到平均速率;然后趨于平均速率(梯形下降到平均速率)灾票∠恳辏可以通過調(diào)節(jié)warmupPeriod參數(shù)實(shí)現(xiàn)一開始就是平滑固定速率。

到此應(yīng)用級(jí)限流的一些方法就介紹完了刊苍。假設(shè)將應(yīng)用部署到多臺(tái)機(jī)器既们,應(yīng)用級(jí)限流方式只是單應(yīng)用內(nèi)的請(qǐng)求限流,不能進(jìn)行全局限流正什。因此我們需要分布式限流和接入層限流來解決這個(gè)問題啥纸。

分布式限流

分布式限流最關(guān)鍵的是要將限流服務(wù)做成原子化,而解決方案可以使使用redis+lua或者nginx+lua技術(shù)進(jìn)行實(shí)現(xiàn)婴氮,通過這兩種技術(shù)可以實(shí)現(xiàn)的高并發(fā)和高性能斯棒。

首先我們來使用redis+lua實(shí)現(xiàn)時(shí)間窗內(nèi)某個(gè)接口的請(qǐng)求數(shù)限流,實(shí)現(xiàn)了該功能后可以改造為限流總并發(fā)/請(qǐng)求數(shù)和限制總資源數(shù)主经。Lua本身就是一種編程語言荣暮,也可以使用它實(shí)現(xiàn)復(fù)雜的令牌桶或漏桶算法。

redis+lua實(shí)現(xiàn)中的lua腳本:

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

local key = KEYS[1] --限流KEY(一秒一個(gè))

local limit = tonumber(ARGV[1])? ? ? ? --限流大小

local current = tonumber(redis.call("INCRBY", key, "1")) --請(qǐng)求數(shù)+1

if current > limit then --如果超出限流大小

return 0

elseif current == 1 then? --只有第一次訪問需要設(shè)置2秒的過期時(shí)間

redis.call("expire", key,"2")

end

return 1

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

如上操作因是在一個(gè)lua腳本中旨怠,又因Redis是單線程模型渠驼,因此是線程安全的。如上方式有一個(gè)缺點(diǎn)就是當(dāng)達(dá)到限流大小后還是會(huì)遞增的鉴腻,可以改造成如下方式實(shí)現(xiàn):

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

local key = KEYS[1] --限流KEY(一秒一個(gè))

local limit = tonumber(ARGV[1])? ? ? ? --限流大小

local current = tonumber(redis.call('get', key) or "0")

if current + 1 > limit then --如果超出限流大小

return 0

else? --請(qǐng)求數(shù)+1迷扇,并設(shè)置2秒過期

redis.call("INCRBY", key,"1")

redis.call("expire", key,"2")

return 1

end

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

如下是Java中判斷是否需要限流的代碼:

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

public static boolean acquire() throws Exception {

String luaScript = Files.toString(new File("limit.lua"), Charset.defaultCharset());

Jedis jedis = new Jedis("192.168.147.52", 6379);

String key = "ip:" + System.currentTimeMillis()/ 1000; //此處將當(dāng)前時(shí)間戳取秒數(shù)

Stringlimit = "3"; //限流大小

return (Long)jedis.eval(luaScript,Lists.newArrayList(key), Lists.newArrayList(limit)) == 1;

}

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

因?yàn)镽edis的限制(Lua中有寫操作不能使用帶隨機(jī)性質(zhì)的讀操作百揭,如TIME)不能在Redis Lua中使用TIME獲取時(shí)間戳,因此只好從應(yīng)用獲取然后傳入蜓席,在某些極端情況下(機(jī)器時(shí)鐘不準(zhǔn)的情況下)器一,限流會(huì)存在一些小問題。

使用Nginx+Lua實(shí)現(xiàn)的Lua腳本:

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

local locks = require "resty.lock"

local function acquire()

local lock =locks:new("locks")

local elapsed, err =lock:lock("limit_key") --互斥鎖

local limit_counter =ngx.shared.limit_counter --計(jì)數(shù)器

local key = "ip:" ..os.time()

local limit = 5 --限流大小

local current =limit_counter:get(key)

if current ~= nil and current + 1> limit then --如果超出限流大小

lock:unlock()

return 0

end

if current == nil then

limit_counter:set(key, 1, 1) --第一次需要設(shè)置過期時(shí)間厨内,設(shè)置key的值為1祈秕,過期時(shí)間為1秒

else

limit_counter:incr(key, 1) --第二次開始加1即可

end

lock:unlock()

return 1

end

ngx.print(acquire())

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

實(shí)現(xiàn)中我們需要使用lua-resty-lock互斥鎖模塊來解決原子性問題(在實(shí)際工程中使用時(shí)請(qǐng)考慮獲取鎖的超時(shí)問題),并使用ngx.shared.DICT共享字典來實(shí)現(xiàn)計(jì)數(shù)器雏胃。如果需要限流則返回0请毛,否則返回1。使用時(shí)需要先定義兩個(gè)共享字典(分別用來存放鎖和計(jì)數(shù)器數(shù)據(jù)):

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

http {

……

lua_shared_dict locks 10m;

lua_shared_dict limit_counter 10m;

}

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

有人會(huì)糾結(jié)如果應(yīng)用并發(fā)量非常大那么redis或者nginx是不是能抗得撞t亮》椒拢徊贿^這個(gè)問題要從多方面考慮:你的流量是不是真的有這么大,是不是可以通過一致性哈希將分布式限流進(jìn)行分片统翩,是不是可以當(dāng)并發(fā)量太大降級(jí)為應(yīng)用級(jí)限流仙蚜;對(duì)策非常多,可以根據(jù)實(shí)際情況調(diào)節(jié)厂汗;像在京東使用Redis+Lua來限流搶購流量委粉,一般流量是沒有問題的。

對(duì)于分布式限流目前遇到的場(chǎng)景是業(yè)務(wù)上的限流娶桦,而不是流量入口的限流贾节;流量入口限流應(yīng)該在接入層完成,而接入層筆者一般使用Nginx趟紊。

參考資料

https://en.wikipedia.org/wiki/Token_bucket

https://en.wikipedia.org/wiki/Leaky_bucket

http://redis.io/commands/incr

http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

https://github.com/openresty/lua-resty-limit-traffic

http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate

一個(gè)有趣有料的架構(gòu)師分享平臺(tái)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末氮双,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子霎匈,更是在濱河造成了極大的恐慌,老刑警劉巖送爸,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铛嘱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡袭厂,警方通過查閱死者的電腦和手機(jī)墨吓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纹磺,“玉大人帖烘,你說我怎么就攤上這事¢涎睿” “怎么了秘症?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵照卦,是天一觀的道長。 經(jīng)常有香客問我乡摹,道長役耕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任聪廉,我火速辦了婚禮瞬痘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘板熊。我一直安慰自己框全,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布干签。 她就那樣靜靜地躺著竣况,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筒严。 梳的紋絲不亂的頭發(fā)上丹泉,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音鸭蛙,去河邊找鬼摹恨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛娶视,可吹牛的內(nèi)容都是我干的晒哄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肪获,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼寝凌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孝赫,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤较木,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后青柄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伐债,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年致开,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峰锁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡双戳,死狀恐怖虹蒋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤魄衅,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布峭竣,位于F島的核電站,受9級(jí)特大地震影響徐绑,放射性物質(zhì)發(fā)生泄漏邪驮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一傲茄、第九天 我趴在偏房一處隱蔽的房頂上張望毅访。 院中可真熱鬧,春花似錦盘榨、人聲如沸喻粹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽守呜。三九已至,卻和暖如春山憨,著一層夾襖步出監(jiān)牢的瞬間查乒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工郁竟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玛迄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓棚亩,卻偏偏與公主長得像蓖议,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讥蟆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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