java各层级限流对比,面试官说:来谈谈限流-从概念到实现,一问你就懵逼了?...
后端服務(wù)的接口都是有訪問上限的,如果外部qps或并發(fā)量超過了訪問上限會(huì)導(dǎo)致應(yīng)用癱瘓。所以一般都會(huì)對(duì)接口調(diào)用加上限流保護(hù),防止超出預(yù)期的請(qǐng)求導(dǎo)致系統(tǒng)故障。
從限流類型來說一般來說分為兩種:并發(fā)數(shù)限流和qps限流,并發(fā)數(shù)限流就是限制同一時(shí)刻的最大并發(fā)請(qǐng)求數(shù)量,qps限流指的是限制一段時(shí)間內(nèi)發(fā)生的請(qǐng)求個(gè)數(shù)。
從作用范圍的層次上來看分單機(jī)限流和分布式限流,前者是針對(duì)單機(jī)的,后者是針對(duì)集群的,他們的思想都是一樣的,只不過是范圍不一樣,本文分析的都是單機(jī)限流。
接下來我們看看并發(fā)數(shù)限流和qps限流。
并發(fā)數(shù)限流
并發(fā)數(shù)限流限制的是同一時(shí)刻的并發(fā)數(shù),所以不考慮線程安全的話,我們只要用一個(gè)int變量就能實(shí)現(xiàn),偽代碼如下:
int maxrequest=100;
int nowrequest=0;
public void request(){
if(nowrequest>=maxrequest){
return ;
}
nowrequest++;
//調(diào)用接口
try{
invokexxx();
}finally{
nowrequest--;
}
}
顯然,上述實(shí)現(xiàn)會(huì)有線程安全的問題,最直接的做法是加鎖:
int maxrequest=100;
int nowrequest=0;
public void request(){
if(nowrequest>=maxrequest){
return ;
}
synchronized(this){
if(nowrequest>=maxrequest){
return ;
}
nowrequest++;
}
//調(diào)用接口
try{
invokexxx();
}finally{
synchronized(this){
nowrequest--;
}
}
}
當(dāng)然也可以用atomicinteger實(shí)現(xiàn):
int maxrequest=100;
atomicinteger nowrequest=new atomicinteger(0);
public void request(){
for(;;){
int currentreq=nowrequest.get();
if(currentreq>=maxrequest){
return;
}
if(nowrequest.compareandset(currentreq,currentreq+1)){
break;
}
}
//調(diào)用接口
try{
invokexxx();
}finally{
nowrequest.decrementandget();
}
}
熟悉jdk并發(fā)包的同學(xué)會(huì)說干嘛這么麻煩,這不就是信號(hào)量(semaphore)做的事情嗎? 對(duì)的,其實(shí)最簡(jiǎn)單的方法就是用信號(hào)量來實(shí)現(xiàn):
int maxrequest=100;
semaphore reqsemaphore = new semaphore(maxrequest);
public void request(){
if(!reqsemaphore.tryacquire()){
return ;
}
//調(diào)用接口
try{
invokexxx();
}finally{
reqsemaphore.release();
}
}
條條大路通羅馬,并發(fā)數(shù)限流比較簡(jiǎn)單,一般來說用信號(hào)量就好。
qps限流
qps限流限制的是一段時(shí)間內(nèi)(一般指1秒)的請(qǐng)求個(gè)數(shù)。
計(jì)數(shù)器法
最簡(jiǎn)單的做法用一個(gè)int型的count變量做計(jì)數(shù)器:請(qǐng)求前計(jì)數(shù)器+1,如超過閾值并且與第一個(gè)請(qǐng)求的間隔還在1s內(nèi),則限流。
偽代碼如下:
int maxqps=100;
int count;
long timestamp=system.currenttimemillis();
long interval=1000;
public synchronized boolean grant(){
long now=system.currenttimemillis();
if(now
count++;
return count
}else{
timestamp=now;
count=1;
return true;
}
}
該種方法實(shí)現(xiàn)起來很簡(jiǎn)單,但其實(shí)是有臨界問題的,假如在第一秒的后500ms來了100個(gè)請(qǐng)求,第2秒的前500ms來了100個(gè)請(qǐng)求,那在這1秒內(nèi)其實(shí)最大qps為200。如下圖:
計(jì)數(shù)器法會(huì)有臨界問題,主要還是統(tǒng)計(jì)的精度太低,這點(diǎn)可以通過滑動(dòng)窗口算法解決
滑動(dòng)窗口
我們用一個(gè)長(zhǎng)度為10的數(shù)組表示1秒內(nèi)的qps請(qǐng)求,數(shù)組每個(gè)元素對(duì)應(yīng)了相應(yīng)100ms內(nèi)的請(qǐng)求數(shù)。用一個(gè)sum變量代碼當(dāng)前1s的請(qǐng)求數(shù)。同時(shí)每隔100ms將淘汰過期的值。
偽代碼如下:
int maxqps=100;
atomicinteger[] count=new atomicinteger[10];
long timestamp=system.currenttimemillis();
long interval=1000;
atomicinteger sum;
volatile int index;
public void init(){
for(int i=0;i
count[i]=new atomicinteger(0);
}
sum=new atomicinteger(0);
}
public synchronized boolean grant(){
count[index].incrementandget();
return sum.incrementandget()
}
//每100ms執(zhí)行一次
public void run(){
index=(index+1)%count.length;
int val=count[index].getandset(0);
sum.addandget(-val);
}
滑動(dòng)窗口的窗口越小,則精度越高,相應(yīng)的資源消耗也更高。
漏桶算法
漏桶算法思路是,有一個(gè)固定大小的桶,水(請(qǐng)求)忽快忽慢的進(jìn)入到漏桶里,漏桶以一定的速度出水。當(dāng)桶滿了之后會(huì)發(fā)生溢出。
在維基百科上可以看到,漏桶算法有兩種實(shí)現(xiàn),一種是as a meter,另一種是as a queue。網(wǎng)上大多數(shù)文章都沒有提到其有兩種實(shí)現(xiàn),且對(duì)這兩種概念混亂。
as a meter
第一種實(shí)現(xiàn)是和令牌桶等價(jià)的,只是表述角度不同。
偽代碼如下:
long timestamp=system.currenttimemillis();//上一次調(diào)用grant的時(shí)間
int bucketsize=100;//桶大小
int rate=10;//每ms流出多少請(qǐng)求
int count;//目前的水量
public synchronized boolean grant(){
long now = system.currenttimemillis();
if(now>timestamp){
count = math.max(0,count-(now-timestamp)*rate);
timestamp = now;
}
if(count+1<=bucketsize){
count++;
return true;
}else{
return false;
}
}
該種實(shí)現(xiàn)允許一段時(shí)間內(nèi)的突發(fā)流量,比如初始時(shí)桶中沒有水,這時(shí)1ms內(nèi)來了100個(gè)請(qǐng)求,這100個(gè)請(qǐng)求是不會(huì)被限流的,但之后每ms最多只能接受10個(gè)請(qǐng)求(比如下1ms又來了100個(gè)請(qǐng)求,那其中90個(gè)請(qǐng)求是會(huì)被限流的)。
其達(dá)到的效果和令牌桶一樣。
as a queue
第二種實(shí)現(xiàn)是用一個(gè)隊(duì)列實(shí)現(xiàn),當(dāng)請(qǐng)求到來時(shí)如果隊(duì)列沒滿則加入到隊(duì)列中,否則拒絕掉新的請(qǐng)求。同時(shí)會(huì)以恒定的速率從隊(duì)列中取出請(qǐng)求執(zhí)行。
偽代碼如下:
queue queue=new linkedblockingqueue(100);
int gap;
int rate;
public synchronized boolean grant(request req){
if(!queue.offer(req)){return false;}
}
// 單獨(dú)線程執(zhí)行
void consume(){
while(true){
for(int i=0;i
//執(zhí)行請(qǐng)求
request req=queue.poll();
if(req==null){break;}
req.dorequest();
}
thread.sleep(gap);
}
}
對(duì)于該種算法,固定的限定了請(qǐng)求的速度,不允許流量突發(fā)的情況。
比如初始時(shí)桶是空的,這時(shí)1ms內(nèi)來了100個(gè)請(qǐng)求,那只有前10個(gè)會(huì)被接受,其他的會(huì)被拒絕掉。注意與上文中as a meter實(shí)現(xiàn)的區(qū)別。
**不過,當(dāng)桶的大小等于每個(gè)ticket流出的水大小時(shí),第二種漏桶算法和第一種漏桶算法是等價(jià)的。**也就是說,as a queue是as a meter的一種特殊實(shí)現(xiàn)。如果你沒有理解這句話,你可以再看看上面as a meter的偽代碼,當(dāng)bucketsize==rate時(shí),請(qǐng)求速度就是恒定的,不允許突發(fā)流量。
令牌桶算法
令牌桶算法的思想就是,桶中最多有n個(gè)令牌,會(huì)以一定速率往桶中加令牌,每個(gè)請(qǐng)求都需要從令牌桶中取出相應(yīng)的令牌才能放行,如果桶中沒有令牌則被限流。
令牌桶算法與上文的漏桶算法as a meter實(shí)現(xiàn)是等價(jià)的,能夠在限制數(shù)據(jù)的平均傳輸速率的同時(shí)還允許某種程度的突發(fā)傳輸。偽代碼:
int token;
int bucketsize;
int rate;
long timestamp=system.currenttimemillis();
public synchronized boolean grant(){
long now=system.currenttimemillis();
if(now>timestamp){
token=math.max(bucketsize,token+(timestamp-now)*rate);
timestamp=now;
}
if(token>0){
token--;
return true;
}else{
return false;
}
}
漏桶算法兩種實(shí)現(xiàn)和令牌桶算法的對(duì)比
as a meter的漏桶算法和令牌桶算法是一樣的,只是思想角度有所不同。
as a queue的漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率,而令牌桶和as a meter漏桶則能夠在限制數(shù)據(jù)的平均傳輸速率的同時(shí)還允許某種程度的突發(fā)傳輸。
一般業(yè)界用的比較多的是令牌桶算法,像guava中的ratelimiter就是基于令牌桶算法實(shí)現(xiàn)的。當(dāng)然不同的業(yè)務(wù)場(chǎng)景會(huì)有不同的需要,具體的選擇還是要結(jié)合場(chǎng)景。
end
本文介紹了后端系統(tǒng)中常用的限流算法,對(duì)于每種算法都有對(duì)應(yīng)的偽代碼,結(jié)合偽代碼理解起來應(yīng)該不難。但偽代碼中只是描述了大致思想,對(duì)于一些細(xì)節(jié)和效率問題并沒有關(guān)注,所以下篇文章將會(huì)分析常用限流api:guava的ratelimiter的源碼實(shí)現(xiàn),讓讀者對(duì)于限流有個(gè)更清晰的認(rèn)識(shí)。
本人免費(fèi)整理了java高級(jí)資料,涵蓋了java、redis、mongodb、mysql、zookeeper、spring cloud、dubbo高并發(fā)分布式等教程,一共30g,需要自己領(lǐng)取。
希望與廣大網(wǎng)友互動(dòng)??
點(diǎn)此進(jìn)行留言吧!
總結(jié)
以上是生活随笔為你收集整理的java各层级限流对比,面试官说:来谈谈限流-从概念到实现,一问你就懵逼了?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 广发diy信用卡额度多少 提额技巧全解析
- 下一篇: 一个列中多行求和_excel表格制作,E