通常在高並發和大流量的情況下,一般限流是必須的。為了保證服務器正常的壓力。那我們就聊一下幾種限流的算法。
- 計數器
計數器是一種最常用的一種方法,在一段時間間隔內,處理請求的數量固定的,超的就不做處理。
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);
}
}