php 基於redis使用令牌桶演算法 計數器 漏桶演算法 實現流量控制
阿新 • • 發佈:2020-09-03
通常在高併發和大流量的情況下,一般限流是必須的。為了保證伺服器正常的壓力。那我們就聊一下幾種限流的演算法。
- 計數器
計數器是一種最常用的一種方法,在一段時間間隔內,處理請求的數量固定的,超的就不做處理。
demo
public function SpeedCounter() { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); // 最大請求數量 $maxCount = 100; //每分鐘內,一個使用者只能訪問10次 $interval =60; //請求總數量 $zcount = $redis->incr('zcont'); //判斷是否超過最大值 if ($zcount<=$maxCount) { //業務處理 $user = [ 11,21,31,41,51,61 ]; foreach ($user as $val) { $key = $val; $check = $redis->exists($key); if ($check) { $sum = $redis->incr($key); if ($sum<=5){ //業務處理 echo "每個使用者在規定的時間內只能訪問5次 $sum"; } else { echo "你已經購買過 $sum"; } } else { //print_r($redis->get($key)) ; ///請購買 echo "請購買"; $sum = $redis->incr($key); $redis->Expire($key,$interval); } } } else { //超過請求數量 $redis->Expire('zcont',$interval); echo '超出請求'.$zcount; }
- 漏桶演算法
漏桶的大小是固定的,處理速度也是固定的,但是請求的速率的不固定的。在突發的情況下,會丟棄很多請求。
/** * **漏桶的大小是固定的,處理速度也是固定的,但是請求的速率的不固定的。在突發的情況下,會丟棄很多請求。** */ function LeackBucket() { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); //桶的容量 $maxCount = 1000; //時間 $interval = 10; //每分鐘流出的數量 $speed = 20; //使用者 $time = $redis->time(); $key = $time[0].$time[1]; //時間判斷 //$redis->del('outCount'); $check = $redis->exists('outCount'); // echo $check; if ($check){ //出桶的速率的請求數量 $outCount = $redis->incr('outCount'); if ($outCount<=$speed){ //業務處理 echo "規定的時間內只能訪問20次 $outCount"; } else { echo "你已經超過每分鐘的訪問 $outCount"; } } else { $outCount = $redis->incr('outCount'); // echo $outCount; $redis->Expire('outCount',$interval); echo "時間過了";exit; } }
- 令牌桶
令牌桶演算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的演算法,更加容易理解.隨著時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務.
令牌桶的另外一個好處是可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種演算法則實時的計算應該增加的令牌的數量.
/** * 令牌 */ function TrafficShaper(){ $redis = new \Redis(); $redis->pconnect('127.0.0.1', 6379); //桶的容量 $maxCount = 10; //當前容量 $curnum = $maxCount-$redis->get('token')-1; echo $curnum; if ($curnum>0){ //業務邏輯 //成功後 $token = $redis->incr('token'); echo "===$token"; } else { echo "沒有令牌了"; $redis->set('token',0); } }