漏斗算法
漏桶算法的原理:
漏桶有一定的容量,给漏桶注水,当单位时间内注入水量大于流出水量,漏桶内积累的水就会越来越多,直到溢出。
就好比大批量请求访问nginx相当于注水,nginx根据配置按照固定速率处理请求当做排水。
漏桶容量就好比配置给nginx的队列长度。当漏桶发生溢出,则禁止请求进入,直接返回错误
php实现一个简单的漏斗算法
/**
* [leaky php实现漏桶算法]
* @param [type] $contain [int 桶的总容量]
* @param [type] $addNum [int 每次注入桶中的水量]
* @param [type] $leakRate [int 桶中漏水的速率,秒为单位。例如2/s,3/s]
* @param integer &$water [int 当前水量]
* @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);
}
redis 的漏斗算法实现
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);
}
}