Как ограничить использование API на моем сайте?
У меня есть проблема: легитимные пользователи моего сайта иногда слишком часто отправляют запросы к API, что приводит к нежелательным последствиям. Я хочу установить ограничение, чтобы не происходило более одного запроса к API каждые 5 секунд или n запросов в минуту (конкретный лимит еще не определен). Я могу, конечно, регистрировать каждый запрос к API в базе данных и проверять при каждом запросе, превышает ли пользователь лимит, но такое дополнительное нагрузка на каждый запрос может свести на нет всю суть решения проблемы. Какие существуют менее ресурсоемкие методы, которые я мог бы использовать для установки ограничения? Я использую PHP/Apache/Linux, если это имеет значение.
5 ответ(ов)
Привет! В ответ на твой вопрос: да, действительно, избежать записей на сервер при отслеживании запросов невозможно, но есть возможность минимизировать количество таких операций. Один из способов - это использовать метод "протекающего ведра" (leaky bucket), который позволяет отслеживать только последний запрос ($last_api_request
) и соотношение количества запросов к лимиту за заданный временной интервал ($minute_throttle
). В отличие от ограничения Twitter API, которое сбрасывается каждый час, ведро в данном случае не обнуляет счетчик. Если ведро заполняется (пользователь достиг предела), ему придется подождать n
секунд, пока ведро немного опустеет, прежде чем он сможет сделать новый запрос. Это как скользящий лимит: если есть предыдущие запросы в пределах временного интервала, они медленно "утекают" из ведра, и ограничение применяется только в случае, если ведро переполнено.
Приведенный ниже код будет рассчитывать новое значение $minute_throttle
для каждого запроса. Я указал минуту в $minute_throttle
, потому что можно добавлять ограничения для любого времени, такого как час, день и т. д., хотя многопоточность может стать запутанной для пользователей.
$minute = 60;
$minute_limit = 100; # пользователи ограничены 100 запросами/минуту
$last_api_request = $this->get_last_api_request(); # получаем из БД; в эпохальных секундах
$last_api_diff = time() - $last_api_request; # в секундах
$minute_throttle = $this->get_throttle_minute(); # получаем из БД
if ( is_null( $minute_limit ) ) {
$new_minute_throttle = 0;
} else {
$new_minute_throttle = $minute_throttle - $last_api_diff;
$new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
$new_minute_throttle += $minute / $minute_limit;
$minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute );
# при необходимости можно вывести это значение с запросом:
$minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}
if ( $new_minute_throttle > $minute ) {
$wait = ceil( $new_minute_throttle - $minute );
usleep( 250000 );
throw new My_Exception ( 'Превышен лимит API за одну минуту в ' . $minute_limit
. ' запросов. Пожалуйста, подождите ' . $wait . ' секунд перед новой попыткой.' );
}
# Сохраняем значения обратно в базу данных.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );
Этот код эффективно организует управление запросами и минимизирует количество записей на сервере. Надеюсь, это поможет!
Самое простое решение заключается в том, чтобы ограничить количество запросов для каждого API-ключа до определённого числа в сутки и сбрасывать этот лимит в известное, фиксированное время.
Если пользователи исчерпают свои доступные запросы (т.е. счётчик достигнет нуля или лимита, в зависимости от направления счёта), следует прекратить предоставление им данных, пока не произойдёт сброс счётчика.
Таким образом, будет в их интересах не перегружать вас запросами.
Данный поток, возможно, уже неактивен, но я бы предложил хранить статистику в кэш-памяти, например, используя memcached. Это поможет снизить нагрузку на запись запросов в базу данных, при этом сохраняя функциональность.
Помимо реализации с нуля, вы можете также рассмотреть использование инфраструктуры API, такой как 3scale (http://www.3scale.net), которая предоставляет функции ограничения скорости (rate limiting), а также множество других возможностей (например, аналитика). Для этого существует плагин на PHP: https://github.com/3scale/3scale_ws_api_for_php.
Кроме того, вы можете использовать Varnish перед API для выполнения ограничения скорости таким образом.
Да, это действительно можно сделать довольно просто с помощью сессий. Вместо того чтобы использовать microtime()
для отслеживания времени последнего доступа, вы можете сохранить это значение в сессии. Например, вы можете сравнить текущее время, используя microtime()
, с временем последнего доступа, которое хранится в $_SESSION['last_access_microtime']
.
Вот пример кода, который демонстрирует этот подход:
session_start();
// Проверяем, существует ли значение времени последнего доступа в сессии
if (isset($_SESSION['last_access_microtime'])) {
// Получаем текущее время
$current_time = microtime(true);
// Сравниваем текущее время с временем последнего доступа
if ($current_time - $_SESSION['last_access_microtime'] > 60) { // Например, 60 секунд
echo "Прошло более 60 секунд с последнего доступа.";
} else {
echo "Последний доступ был менее 60 секунд назад.";
}
}
// Обновляем время последнего доступа
$_SESSION['last_access_microtime'] = microtime(true);
Таким образом, вы можете легко отслеживать время последнего доступа пользователя, храня его в сессии и сравнивая с текущим временем. Это простой и эффективный способ управления состоянием пользователя.
UTF-8 на всех уровнях!
Где найти файл php.ini?
Разница между HTTP_HOST и SERVER_NAME в PHP
Узнайте, как PHP работает на сервере (CGI, FastCGI или mod_php)
Как изменить цвет вывода echo в Linux