php+redis實現(xiàn)令牌桶算法代碼:
<?phpnamespace ApiLib;/** * 限流控制 */class RateLimit{ private $minNum = 60; //單個用戶每分訪問數(shù) private $dayNum = 10000; //單個用戶每天總的訪問量 public function minLimit($uid) { $minNumKey = $uid . '_minNum'; $dayNumKey = $uid . '_dayNum'; $resMin = $this->getRedis($minNumKey, $this->minNum, 60); $resDay = $this->getRedis($minNumKey, $this->minNum, 86400); if (!$resMin['status'] || !$resDay['status']) { exit($resMin['msg'] . $resDay['msg']); } } public function getRedis($key, $initNum, $expire) { $nowtime = time(); $result = ['status' => true, 'msg' => '']; $redisObj = $this->di->get('redis'); $redis->watch($key); $limitVal = $redis->get($key); if ($limitVal) { $limitVal = json_decode($limitVal, true); $newNum = min($initNum, ($limitVal['num'] - 1) + (($initNum / $expire) * ($nowtime - $limitVal['time']))); if ($newNum > 0) { $redisVal = json_encode(['num' => $newNum, 'time' => time()]); } else { return ['status' => false, 'msg' => '當前時刻令牌消耗完!']; } } else { $redisVal = json_encode(['num' => $initNum, 'time' => time()]); } $redis->multi(); $redis->set($key, $redisVal); $rob_result = $redis->exec(); if (!$rob_result) { $result = ['status' => false, 'msg' => '訪問頻次過多!']; } return $result; }}
代碼要點:
1、首先定義規(guī)則
單個用戶每分鐘訪問次數(shù)($minNum),單個用戶每天總的訪問次數(shù)($dayNum),接口總的訪問次數(shù)等不同的規(guī)則。
2、計算速率
該代碼示例以秒為最小的時間單位,速率=訪問次數(shù)/時間($initNum / $expire)
3、每次訪問后補充的令牌個數(shù)計算方式
獲取上次訪問的時間即上次存入令牌的時間,計算當前時刻與上次訪問的時間差乘以速率就是此次需要補充的令牌個數(shù),注意補充令牌后總的令牌個數(shù)不能大于初始化的令牌個數(shù),以補充數(shù)和初始化數(shù)的最小值為準。
4、程序流程
第一次訪問時初始化令牌個數(shù)($minNum),存入Redis同時將當前的時間戳存入以便計算下次需要補充的令牌個數(shù)。
第二次訪問時獲取剩余的令牌個數(shù),并添加本次應(yīng)該補充的令牌個數(shù),補充后如何令牌數(shù)>0則當前訪問是有效的可以訪問,否則令牌使用完畢不可訪問。先補充令牌再判斷令牌是否>0的原因是由于還有速率這個概念即如果上次剩余的令牌為0但是本次應(yīng)該補充的令牌>1那么本次依然可以訪問。
5、針對并發(fā)的處理
使用Redis的樂觀鎖機制。
更多相關(guān)知識,請關(guān)注 PHP中文網(wǎng)!!
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com