123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- <?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());
- }
- }
|