漏桶算法-php實現
1,概覽
最近研究nginx的限流,limit_req_zone。
其功能就是限制大訪問量下的請求數量,防止服務器故障。
核心邏輯就是:
1,給nginx配置一個處理請求速率。比如每秒處理5個請求。
2,大訪問量下沒有處理到的請求進行排隊等待。
3,給排隊的請求配置一個長度,超過了長度的請求直接返回錯誤 。比如設置隊列長度爲5,有5個請求正在排隊的情況下,
下一個請求直接返回錯誤。
比較感興趣它的實現,查閱資料發現利用了漏桶算法。就順便帶着學習一下。
漏桶算法的原理:
漏桶有一定的容量,給漏桶注水,當單位時間內注入水量大於流出水量,漏桶內積累的水就會越來越多,直到溢出。
就好比大批量請求訪問nginx相當於注水,nginx根據配置按照固定速率處理請求當做排水。
漏桶容量就好比配置給nginx的隊列長度。當漏桶發生溢出,則禁止請求進入,直接返回錯誤。
2,實現
/**
* [leaky php實現漏桶算法]
* @Author [NiuShao <[email protected]> <qq:370574131>]
* @DateTime 2020-06-26
* @param [type] $contain [int 桶的總容量]
* @param [type] $addNum [int 每次注入桶中的水量]
* @param [type] $leakRate [int 桶中漏水的速率,秒爲單位。例如2/s,3/s]
* @param integer &$water [int 當前水量,默認爲0]
* @param integer &$preTime [int 時間戳,記錄的上次漏水時間]
* @return [type] [bool,返回可否繼續注入true/false]
*/
function leaky($contain,$addNum,$leakRate,&$water=0,&$preTime=0)
{
//參數賦值
//首次進入默認當前水量爲0
$water = empty($water) ? 0 : $water;
//首次進入默認上次漏水時間爲當前時間
$preTime = empty($water) ? time() : $preTime;
$curTime = time();
//上次結束到本次開始,流出去的水
$leakWater = ($curTime-$preTime)*$leakRate;
//上次結束時候的水量減去流出去的水,也就是本次初始水量
$water = $water-$leakWater;
//水量不可能爲負,漏出大於進入則水量爲0
$water = ( $water>=0 ) ? $water : 0 ;
//更新本次漏完水的時間
$preTime = $curTime;
//水小於總容量則可注入,否則不可注入
if( ($water+$addNum) <= $contain ){
$water += $addNum;
return true;
}else{
return false;
}
}
/**
* 測試
* @var integer
*/
for($i=0;$i<500;$i++){
$res = leaky(50,1,5,$water,$timeStamp);
var_dump($res);
usleep(50000);
}
3,說明:
上面的漏桶算法更加的一般化了,容器總量,注水量,排水量都可以隨機設置。
爲了完全使用php實現,時間參數$preTime和當前水量$water都利用了php的引用賦值。作爲全局變量。
測試過程利用率循環機制給漏桶注水。
在實際生產環境下如果要用php代碼做限流,容器總量,注水排水都不能爲小數。
並且對時間參數和當前水量建議用redis緩存或者文件緩存來進行保存。
因爲(nginx+php-fpm)架構下,每次請求都是獨立的,所有變量都會被釋放。