限流策略通常是用來(lái)在高qps下進(jìn)行流量限制的,常見(jiàn)的方式有計(jì)數(shù)器罕扎、令牌桶丐重、漏桶。在這次活動(dòng)中我負(fù)責(zé)的模塊是控制的對(duì)下游的流量臀蛛,我們可以讓那些請(qǐng)求選擇丟棄崖蜜、等待或者降級(jí)這些限流算法可以自行實(shí)現(xiàn)也可以利用現(xiàn)有的限流工具豫领,比如說(shuō)Guava的令牌桶等恐,具體看場(chǎng)景需求吧备蚓,下面來(lái)看一下這幾種限流策略囱稽,再說(shuō)說(shuō)我寫(xiě)的限流方式战惊。
1、計(jì)數(shù)器限流
這種方式比較粗暴横缔,相當(dāng)于有一個(gè)計(jì)數(shù)器來(lái)控制單秒請(qǐng)求數(shù)茎刚,也就是qps定義的方式撤逢。比如說(shuō)限流2000,每秒鐘對(duì)于計(jì)數(shù)器進(jìn)行置0操作初狰,當(dāng)一秒內(nèi)到達(dá)2000時(shí)就不再接受請(qǐng)求了奢入。這樣能保證較長(zhǎng)時(shí)間短的流量均勻腥光,但是單秒內(nèi)部實(shí)際上是不均勻的武福,可能這2000個(gè)請(qǐng)求痘番,在前0.1s就處理完成了,后面的都是被丟掉的汞舱,并且峰值qps 可能是達(dá)到2w的昂芜。
2说铃、令牌桶限流
令牌桶限流是指我們可以設(shè)立一個(gè)令牌桶腻扇,然后以固定的速率往令牌桶中添加令牌,令牌桶滿則不添加窒篱。請(qǐng)求到來(lái)時(shí)檢查如果令牌桶中有令牌則取走令牌舶沿,發(fā)起請(qǐng)求括荡。假設(shè)要限定 2000 qps畸冲,則1/2000 的速率向令牌桶添加令牌,也可以1/1000 一次性添加兩個(gè)令牌算行,以此類推州邢。令牌桶在持續(xù)高qps 下是沒(méi)問(wèn)題的量淌,可以把流量限制的比較均勻类少。但是面對(duì)突發(fā)流量時(shí)渔扎,流量桶里是滿的晃痴,可能一瞬間把令牌搶空完成請(qǐng)求倘核,這里的問(wèn)題和計(jì)數(shù)器限流實(shí)際上是一樣的活尊,峰值可能遠(yuǎn)遠(yuǎn)大于2000,所以對(duì)于突發(fā)流量是限不住的深胳。下面看看令牌桶的示意圖:其實(shí)我感覺(jué)令牌桶更像是優(yōu)化后的計(jì)數(shù)器限流舞终,只不過(guò)時(shí)間窗口由1s變的更細(xì)了
3敛劝、漏斗限流
這個(gè)是使用最多的一種限流算法夸盟,通常用來(lái)流量整形或者流量控制满俗,看起來(lái)和令牌桶比較像唆垃,但是差異還是比較大的辕万。漏斗往桶里加的是請(qǐng)求渐尿,不是令牌砖茸,相當(dāng)于新請(qǐng)求到達(dá)時(shí)放到桶中,如果桶滿了則溢出請(qǐng)求劲够,桶以勻速漏出請(qǐng)求進(jìn)行處理征绎,比如qps 2000人柿,則1/2000 s 漏出一個(gè)請(qǐng)求進(jìn)行處理。這種限流方式比較穩(wěn)定拐邪,但是需要維護(hù)一個(gè)請(qǐng)求隊(duì)列或者任務(wù)隊(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ù)得到的限流效果是一樣的荆姆。
看完幾種限流策略原型之后仆救,放到具體的業(yè)務(wù)場(chǎng)景中看算法的選擇及我們需要作出的改動(dòng)。
業(yè)務(wù)端限流:
業(yè)務(wù)端做限流的話,請(qǐng)求來(lái)源于上有系統(tǒng),流量要求是比較平穩(wěn)的,峰值不能太高,否則可能一瞬間打掛系統(tǒng),令牌桶和計(jì)數(shù)器方式就不太合適了汗侵。因?yàn)槿绻髁恐苯哟虻綐I(yè)務(wù)系統(tǒng)我們是沒(méi)法進(jìn)行預(yù)估的,大概率會(huì)有突發(fā)流量,所以選擇直接使用流量桶就比較合適了译仗。
consumer 控制對(duì)下游流量:
我所處的業(yè)務(wù)場(chǎng)景是nginx + lua 打入redis 則認(rèn)為成功缰猴,consumer端消費(fèi)消息疑故,然后持續(xù)固定qps對(duì)下游發(fā)起請(qǐng)求管钳。這種場(chǎng)景下,把消費(fèi)速率&對(duì)下游qps控制放在consumer端來(lái)做就比較合適了。首先我們從Redis或者日志文件中讀取了數(shù)據(jù),并且拼接了請(qǐng)求任務(wù)放到任務(wù)隊(duì)列中。然后線程池從任務(wù)隊(duì)列中取任務(wù)發(fā)起請(qǐng)求,首先我們需要控制加入任務(wù)隊(duì)列的速率,因?yàn)榧尤氲娜蝿?wù)隊(duì)列的速度大于任務(wù)隊(duì)列的消費(fèi)速度,肯定是會(huì)導(dǎo)致OOM產(chǎn)生的,
我這里使用的是類似令牌桶的方式,一個(gè)線程取n個(gè)任務(wù)颤殴,然后線程內(nèi)串行矮瘟,幾個(gè)線程并行埋涧,這樣就一定程度上保證了不會(huì)出現(xiàn)令牌桶的流量不均問(wèn)題了邑跪,同時(shí)減少了鎖的爭(zhēng)用轴踱。實(shí)際上使用漏斗算法也是合適的,但是對(duì)于這個(gè)場(chǎng)景來(lái)說(shuō),溢出任務(wù)實(shí)際上是不太好控制茴肥,需要讓請(qǐng)求的加入速率與消費(fèi)速度相對(duì)保持一致础锐,這一點(diǎn)控制不好很容易o(hù)om的,所以直接采用任務(wù)隊(duì)列 + 令牌桶實(shí)現(xiàn)是最方便控制也是最容易實(shí)現(xiàn)的菊值,采用線程內(nèi)一次取多個(gè),串行發(fā)起請(qǐng)求的方式是可以一定程度上控制住流量的 實(shí)踐證明效果不錯(cuò)。