0

Как ограничить использование API на моем сайте?

18

У меня есть проблема: легитимные пользователи моего сайта иногда слишком часто отправляют запросы к API, что приводит к нежелательным последствиям. Я хочу установить ограничение, чтобы не происходило более одного запроса к API каждые 5 секунд или n запросов в минуту (конкретный лимит еще не определен). Я могу, конечно, регистрировать каждый запрос к API в базе данных и проверять при каждом запросе, превышает ли пользователь лимит, но такое дополнительное нагрузка на каждый запрос может свести на нет всю суть решения проблемы. Какие существуют менее ресурсоемкие методы, которые я мог бы использовать для установки ограничения? Я использую PHP/Apache/Linux, если это имеет значение.

5 ответ(ов)

0

Привет! В ответ на твой вопрос: да, действительно, избежать записей на сервер при отслеживании запросов невозможно, но есть возможность минимизировать количество таких операций. Один из способов - это использовать метод "протекающего ведра" (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 );

Этот код эффективно организует управление запросами и минимизирует количество записей на сервере. Надеюсь, это поможет!

0

Самое простое решение заключается в том, чтобы ограничить количество запросов для каждого API-ключа до определённого числа в сутки и сбрасывать этот лимит в известное, фиксированное время.

Если пользователи исчерпают свои доступные запросы (т.е. счётчик достигнет нуля или лимита, в зависимости от направления счёта), следует прекратить предоставление им данных, пока не произойдёт сброс счётчика.

Таким образом, будет в их интересах не перегружать вас запросами.

0

Данный поток, возможно, уже неактивен, но я бы предложил хранить статистику в кэш-памяти, например, используя memcached. Это поможет снизить нагрузку на запись запросов в базу данных, при этом сохраняя функциональность.

0

Помимо реализации с нуля, вы можете также рассмотреть использование инфраструктуры API, такой как 3scale (http://www.3scale.net), которая предоставляет функции ограничения скорости (rate limiting), а также множество других возможностей (например, аналитика). Для этого существует плагин на PHP: https://github.com/3scale/3scale_ws_api_for_php.

Кроме того, вы можете использовать Varnish перед API для выполнения ограничения скорости таким образом.

0

Да, это действительно можно сделать довольно просто с помощью сессий. Вместо того чтобы использовать 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);

Таким образом, вы можете легко отслеживать время последнего доступа пользователя, храня его в сессии и сравнивая с текущим временем. Это простой и эффективный способ управления состоянием пользователя.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь