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

StackOverflow https://stackoverflow.com/questions/1375501

  •  21-09-2019
  •  | 
  •  

Вопрос

Законные пользователи моего сайта время от времени забивают сервер API-запросами, которые приводят к нежелательным результатам.Я хочу установить ограничение, не превышающее, скажем, одного вызова API каждые 5 секунд или n вызовов в минуту (точный предел еще не выяснил).Очевидно, я мог бы регистрировать каждый вызов API в БД и выполнять вычисления для каждого запроса, чтобы увидеть, превышают ли они лимит, но все эти дополнительные накладные расходы на КАЖДЫЙ запрос сведут на нет цель.Какие еще менее ресурсоемкие методы я мог бы использовать для установления ограничения?Я использую PHP/Apache/Linux, чего бы это ни стоило.

Это было полезно?

Решение

Хорошо, нет возможности сделать то, что я просил, без любой пишет на сервер, но я могу, по крайней мере, исключить регистрацию каждого отдельного запроса.Один из способов — использовать метод регулирования «дырявого ведра», при котором он отслеживает только последний запрос ($last_api_request) и соотношение количества запросов/лимита за период времени ($minute_throttle).Дырявое ведро никогда не сбрасывает свой счетчик (в отличие от дроссельной заслонки Twitter API, которая сбрасывается каждый час), но если ведро заполняется (пользователь достиг предела), ему придется подождать. n секунды, чтобы ведро немного опустело, прежде чем они смогут сделать еще один запрос.Другими словами, это похоже на скользящий предел:если в течение указанного периода времени есть предыдущие запросы, они медленно утекают из корзины;это ограничивает вас только в том случае, если вы наполняете ведро.

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

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
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  );
    # can output this value with the request if desired:
    $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 ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

Другие советы

Вы можете контролировать скорость с помощью алгоритм сегмента токенов, что сравнимо с алгоритмом дырявого ведра.Обратите внимание, что вам придется поделиться состоянием корзины (т. е.количество токенов) по процессам (или любой области, которой вы хотите управлять).Поэтому вы можете подумать о блокировке, чтобы избежать условий гонки.

Хорошие новости:Я сделал все это для вас: дросселирование полосы пропускания/ведро токенов

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate    = new Rate(10, Rate::SECOND);
$bucket  = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

if (!$bucket->consume(1, $seconds)) {
    http_response_code(429);
    header(sprintf("Retry-After: %d", floor($seconds)));
    exit();
}

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

Простейшим решением было бы просто предоставить каждому ключу API ограниченное количество запросов в течение 24 часов и сбросить их в какое-то известное фиксированное время.

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

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

Вы говорите, что «все эти дополнительные накладные расходы на КАЖДЫЙ запрос сведут на нет цель», но я не уверен, что это правильно.Разве цель не предотвратить забивание вашего сервера?Вероятно, я бы реализовал это именно так, поскольку на самом деле требуется только быстрое чтение/запись.Вы даже можете передать проверки сервера API другой базе данных/диску, если вас беспокоит производительность.

Однако, если вам нужны альтернативы, вам следует проверить mod_cband, сторонний модуль Apache, предназначенный для регулирования пропускной способности.Несмотря на то, что он предназначен в первую очередь для ограничения пропускной способности, он также может регулироваться в зависимости от количества запросов в секунду.Я никогда не использовал его, поэтому не уверен, какие результаты вы получите.Был еще один модуль под названием mod-throttle, но этот проект, похоже, сейчас закрыт и никогда не выпускался для чего-либо выше серии Apache 1.3.

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

Вы также можете вставить что-то вроде Varnish перед API и таким образом ограничить скорость API.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top