179 lines
3.6 KiB
PHP
Raw Normal View History

2020-07-02 18:41:47 +08:00
<?php
2020-11-29 09:29:14 +08:00
declare(strict_types=1);
2020-07-02 18:41:47 +08:00
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catcher\library\rate;
use catcher\exceptions\FailedException;
class RateLimiter
{
use Redis;
protected $key;
/**
* 令牌容量
*
* @var int
*/
protected $capacity = 5;
/**
* 每次添加 token 的数量
*
* @var int
*/
protected $eachTokens = 5;
/**
* 添加 token 的时间
*
* @var string
*/
protected $addTokenTimeKey = '_add_token';
/**
* 添加 token 的时间间隔 /s
*
* @var int
*/
protected $interval = 5;
/**
* RateLimiter constructor.
* @param $key
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* 处理
*
* @time 2020年07月02日
* @return void
*/
public function overflow()
{
// 添加 token
if ($this->canAddToken()) {
$this->addTokens();
}
if (!$this->tokens()) {
throw new FailedException('访问限制');
}
// 每次请求拿走一个 token
$this->removeToken();
}
/**
*
*
* @time 2020年07月02日
d * @return void
*/
protected function addTokens()
{
$leftTokens = $this->capacity - $this->tokens();
$tokens = array_fill(0, $leftTokens < $this->eachTokens ? $leftTokens : $this->eachTokens, 1);
$this->getRedis()->lPush($this->key, ...$tokens);
$this->rememberAddTokenTime();
}
/**
* 拿走一个 token
*
* @time 2020年07月02日
* @return void
*/
protected function removeToken()
{
$this->getRedis()->rPop($this->key);
}
/**
* 设置令牌桶数量
*
* @time 2020年07月02日
* @param $capacity
* @return $this
*/
public function setCapacity($capacity)
{
$this->capacity = $capacity;
return $this;
}
/**
* 剩余的 token 数量
*
* @time 2020年07月02日
* @return bool|int
*/
protected function tokens()
{
return $this->getRedis()->lLen($this->key);
}
/**
* 设置时间间隔
*
* @time 2020年07月02日
* @param $seconds
* @return $this
*/
public function setInterval($seconds)
{
$this->interval = $seconds;
return $this;
}
/**
* 是否可以添加 token
*
* @time 2020年07月02日
* @return bool
*/
protected function canAddToken()
{
$currentTime = \time();
$lastAddTokenTime = $this->getRedis()->get($this->key. $this->addTokenTimeKey);
// 如果是满的 则不添加
if ($this->tokens() == $this->capacity) {
return false;
}
return ($currentTime - $lastAddTokenTime) > $this->interval;
}
/**
* 记录添加 token 的时间
*
* @time 2020年07月02日
* @return void
*/
protected function rememberAddTokenTime()
{
$this->getRedis()->set($this->key. $this->addTokenTimeKey, time());
}
}