JaguarJack 94c430f491 update
2020-11-29 09:29:14 +08:00

179 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
// +----------------------------------------------------------------------
// | 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());
}
}