作為后台服務,通常有一個處理極限PPS(packets per second),如果請求超過了這個處理能力,可能會出現“雪崩效應”,因此后台服務需要有過載保護機制。
1、有個簡單的算法可以實現流量控制功能:設置一個單位時間(如1s, 1min)內的最大訪問量,並維護一個單位時間里的計數器。
當訪問請求到達時,先判斷單位控制時間是否已經超時,如果已經超時,重置計數器為0;
否則,將計數器加1,並判斷計數器的值是否超過最大訪問量設置,如超過,則拒絕訪問。
偽代碼如下:
1 long timeStamp=getNowTime(); 2 int reqCount=0; 3 const int maxReqCount=10000;//時間周期內最大請求數 4 const long effectiveDuration=10;//時間控制周期 5 6 bool grant() 7 { 8 long now=getNowTime(); 9 if (now <timeStamp+effectiveDuration) 10 { 11 //在時間控制范圍內 12 reqCount++; 13 return reqCount>maxReqCount;//當前時間范圍內超過最大請求控制數 14 } 15 else 16 { 17 timeStamp=now;//超時后重置 18 reqCount=0; 19 return true; 20 }
21 }
該算法實現確實是實現了“單位時間里最大訪問量控制”這一需求,但是,仔細研究下,發現它在兩個單位時間的臨界值上的處理是有缺陷的。
如:設需要控制的最大請求數為1w, 在第一個單位時間的最后一秒里達到的請求數為1w,接下來第二個單位時間內的第一秒里達到請求數也是1w,由於超時重置發生在兩個單位時間之間,
所以這2w個請求都將通過控制,也就是說在2s里處理2w個請求,與我們設置的10s里1w個請求的需求相違背。
2、令牌桶算法
令牌桶算法的原理是系統會以一個恆定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當桶里沒有令牌可取時,則拒絕服務。從原理上看,令牌桶算法和漏桶算法是相反的,一個“進水”,一個是“漏水”。
long timeStamp=getNowTime(); int capacity; // 桶的容量 int rate ; //令牌放入速度 int tokens; //當前水量 bool grant() { //先執行添加令牌的操作 long now = getNowTime(); tokens = min(capacity, tokens+ (now - timeStamp)*rate); //令牌已用完,拒絕訪問 if(tokens<1) { return false; } else { //還有令牌,領取令牌 timeStamp = now; tokens--; retun true; } }