Делаю интеграцию сайта с сервисом вебинаров webinar.ru. У них ограничение - до 3 запросов / сек. Т.е., если с сайта в течение какой-то одной секунды подключится 4-й пользователь, то вебинар.ру в результате этого подключения вернет ошибку. Посещаемость сайта пока не большая, но планируется его очень активное продвижение. Поэтому не исключаю вероятности того что в дальнейшем с ростом посещаемости может возникнуть ситуация, когда может возникнуть описанная ситуация. Как лучше избавиться от подобной проблемы? Подскажите или хотя бы идею, или, еще лучше, какое-то решение на yii2, если, конечно, такое решение существует.
Были мысли использовать в методе, в котором осуществляется обращение к сервису вебинаров, php-функцию sleep(). Т.е., если сервис вебинаров вернул error, то через, например, 1 сек обратиться к сервису вебинаров повторно. Но очень сомневаюсь, что это хорошее решение.
Ограничение на количество запросов и повторный запрос
Re: Ограничение на количество запросов и повторный запрос
Если это запросы на получение данных (чтение), то можно добавить кэширование ответа.
Если это запросы на выполнение операций (запись), то либо делать sleep при обращении, либо все задачи скидывать в очередь и выполнять там последовательно в одном обработчике со sleep.
Если это запросы на выполнение операций (запись), то либо делать sleep при обращении, либо все задачи скидывать в очередь и выполнять там последовательно в одном обработчике со sleep.
Re: Ограничение на количество запросов и повторный запрос
Дмитрий, спасибо!
Да, запросы на получение списка вебинаров и др. запросы на чтение кэшируются.
Запросы "Запись на вебинар". Буду пробовать sleepЕсли это запросы на выполнение операций (запись), то либо делать sleep при обращении, либо все задачи скидывать в очередь и выполнять там последовательно в одном обработчике со sleep.
-
- Сообщения: 251
- Зарегистрирован: 2014.08.18, 14:01
Re: Ограничение на количество запросов и повторный запрос
Код: Выделить всё
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
-
- Сообщения: 251
- Зарегистрирован: 2014.08.18, 14:01
Re: Ограничение на количество запросов и повторный запрос
Я использую везде. Если много задач то сервер очередей. Если ограничение частоты запросов то бросаю обратно в очередь с delay = $limiter->getTtl(). Если ограничение по пользователям еще то используйте ключи доп.
Если используете guzzle то там есть delay в опциях
Если используете guzzle то там есть delay в опциях