Страница 1 из 1
Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.05, 17:31
yura1976
Делаю интеграцию сайта с сервисом вебинаров webinar.ru. У них ограничение - до 3 запросов / сек. Т.е., если с сайта в течение какой-то одной секунды подключится 4-й пользователь, то вебинар.ру в результате этого подключения вернет ошибку. Посещаемость сайта пока не большая, но планируется его очень активное продвижение. Поэтому не исключаю вероятности того что в дальнейшем с ростом посещаемости может возникнуть ситуация, когда может возникнуть описанная ситуация. Как лучше избавиться от подобной проблемы? Подскажите или хотя бы идею, или, еще лучше, какое-то решение на yii2, если, конечно, такое решение существует.
Были мысли использовать в методе, в котором осуществляется обращение к сервису вебинаров, php-функцию sleep(). Т.е., если сервис вебинаров вернул error, то через, например, 1 сек обратиться к сервису вебинаров повторно. Но очень сомневаюсь, что это хорошее решение.
Re: Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.05, 20:44
ElisDN
Если это запросы на получение данных (чтение), то можно добавить кэширование ответа.
Если это запросы на выполнение операций (запись), то либо делать sleep при обращении, либо все задачи скидывать в очередь и выполнять там последовательно в одном обработчике со sleep.
Re: Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.06, 19:01
yura1976
Дмитрий, спасибо!
ElisDN писал(а): ↑2021.06.05, 20:44
Если это запросы на получение данных (чтение), то можно добавить кэширование ответа.
Да, запросы на получение списка вебинаров и др. запросы на чтение кэшируются.
Если это запросы на выполнение операций (запись), то либо делать sleep при обращении, либо все задачи скидывать в очередь и выполнять там последовательно в одном обработчике со sleep.
Запросы "Запись на вебинар". Буду пробовать sleep
Re: Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.07, 08:30
SiZE
yura1976 писал(а): ↑2021.06.06, 19:01
Запросы "Запись на вебинар". Буду пробовать sleep
sleep без очереди не решит проблему, т.к. конкурентные запросы никуда не денутся (если я конечно правильно понял), т.е. в любом случае очередь нужна.
Re: Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.09, 18:11
german.igortcev
Код: Выделить всё
class RequestLimiter extends Component
{
private Connection $redis;
public int $requestsAllowedPerSecond = 1;
public int $requestsAllowedPerMinute = 60;
public int $requestsAllowedPerDay = 1000;
public string $storageKeyPerSecond = 'executed_per_second';
public string $storageKeyPerMinute = 'executed_per_minute';
public string $storageKeyPerDay = 'executed_per_day';
private int $ttl = 0;
public function __construct( $config = [])
{
parent::__construct($config);
$this->redis = \Yii::$app->redis;
}
/**
* @return int
*/
public function getTtl(): int
{
return $this->ttl;
}
/**
* @param int $ttl
*/
public function setTtl(int $ttl): void
{
$this->ttl = $ttl;
}
/**
* @return int
*/
public function getRequestsAllowedPerSecond(): int
{
return $this->requestsAllowedPerSecond;
}
/**
* @param int $requestsAllowedPerSecond
*/
public function setRequestsAllowedPerSecond(int $requestsAllowedPerSecond): void
{
$this->requestsAllowedPerSecond = $requestsAllowedPerSecond;
}
/**
* @return int
*/
public function getRequestsAllowedPerMinute(): int
{
return $this->requestsAllowedPerMinute;
}
/**
* @param int $requestsAllowedPerMinute
*/
public function setRequestsAllowedPerMinute(int $requestsAllowedPerMinute): void
{
$this->requestsAllowedPerMinute = $requestsAllowedPerMinute;
}
/**
* @return int
*/
public function getRequestsAllowedPerDay(): int
{
return $this->requestsAllowedPerDay;
}
/**
* @param int $requestsAllowedPerDay
*/
public function setRequestsAllowedPerDay(int $requestsAllowedPerDay): void
{
$this->requestsAllowedPerDay = $requestsAllowedPerDay;
}
/**
* @return bool
* @throws \Exception
*/
public function canExecute(): bool
{
return $this->checkSecondLimit() && $this->checkMinuteLimit() && $this->checkDayLimit();
}
/**
* @return bool
*/
public function checkSecondLimit(): bool
{
if (!$this->redis->exists($this->storageKeyPerSecond)) {
$this->redis->set($this->storageKeyPerSecond, 1);
$this->redis->expire($this->storageKeyPerSecond, 1);
$this->setTtl(0);
} else {
$this->redis->incr($this->storageKeyPerSecond);
/** Delete key if key exist on redis but lost expire date for key */
if ($this->redis->ttl($this->storageKeyPerSecond) == '-1') {
$this->redis->del($this->storageKeyPerSecond);
}
if ($this->redis->get($this->storageKeyPerSecond) >= $this->requestsAllowedPerSecond) {
$this->setTtl((int)$this->storageKeyPerSecond);
return false;
}
}
return true;
}
/**
* @return bool
*/
public function checkMinuteLimit(): bool
{
if (!$this->redis->exists($this->storageKeyPerMinute)) {
$this->redis->set($this->storageKeyPerMinute, 1);
$this->redis->expire($this->storageKeyPerMinute, 60);
$this->setTtl(0);
} else {
$this->redis->incr($this->storageKeyPerMinute);
/** Delete key if key exist on redis but lost expire date for key */
if ($this->redis->ttl($this->storageKeyPerMinute) == '-1') {
$this->redis->del($this->storageKeyPerMinute);
}
if ($this->redis->get($this->storageKeyPerMinute) >= $this->requestsAllowedPerMinute) {
$this->setTtl((int)$this->redis->ttl($this->storageKeyPerMinute));
return false;
}
}
return true;
}
/**
* @return bool
* @throws \Exception
*/
public function checkDayLimit(): bool
{
$datetime = new \DateTime('now', new \DateTimeZone('PST'));
$datetime->setTime(23, 59, 59);
if (!$this->redis->exists($this->storageKeyPerDay)) {
$this->redis->set($this->storageKeyPerDay, 1);
$this->redis->expireat($this->storageKeyPerDay, $datetime->getTimestamp());
$this->setTtl(0);
} else {
$this->redis->incr($this->storageKeyPerDay);
if ($this->redis->get($this->storageKeyPerDay) >= $this->requestsAllowedPerDay) {
$this->setTtl((int)$this->redis->ttl($this->storageKeyPerDay));
return false;
}
}
return true;
}
}
Код: Выделить всё
!$requestLimiter->canExecute()
$requestLimiter->getTtl() - delay in seconds when you can execute
Re: Ограничение на количество запросов и повторный запрос
Добавлено: 2021.06.09, 18:16
german.igortcev
Я использую везде. Если много задач то сервер очередей. Если ограничение частоты запросов то бросаю обратно в очередь с delay = $limiter->getTtl(). Если ограничение по пользователям еще то используйте ключи доп.
Если используете guzzle то там есть delay в опциях