Итак, я запрашиваю данные из API.
Пока мой ключ API ограничен:
10 запросов каждые 10 секунд
500 запросов каждые 10 минут
В сущности, я хочу запрашивать определенное значение в каждой игре, в которую играл пользователь.
Это, например, около 300 игр.
Поэтому я должен сделать 300 запросов с моим PHP. Как я могу замедлить их, чтобы соблюдать ограничение скорости?
(Это может занять время, сайт не должен быть быстрым)
Я попробовал sleep (), что привело к сбою моего скрипта. Есть ли другие способы сделать это?
Я предлагаю настроить работу cron, которая выполняется каждую минуту, или даже лучше использовать планирование Laravel, а не использовать sleep или usleep для имитации cron.
Вот некоторая информация об обоих:
https://laravel.com/docs/5.1/scheduling
http://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/
Это звучит как идеальное использование для set_time_limit()
функция. Эта функция позволяет вам указать продолжительность выполнения скрипта в секундах. Например, если вы говорите set_time_limit(45);
в начале вашего скрипта, то скрипт будет работать в общей сложности 45 секунд. Одной из замечательных особенностей этой функции является то, что вы можете позволить вашему скрипту выполняться бесконечно (без ограничения по времени), говоря: set_time_limit(0);
,
Вы можете написать свой скрипт, используя следующую общую структуру:
<?php
// Ignore user aborts and allow the script
// to run forever
ignore_user_abort(true);
set_time_limit(0);
// Define constant for how much time must pass between batches of connections:
define('TIME_LIMIT', 10); // Seconds between batches of API requests
$tLast = 0;
while( /* Some condition to check if there are still API connections that need to be made */ ){
if( timestamp() <= ($tLast + TIME_LIMIT) ){ // Check if TIME_LIMIT seconds have passed since the last connection batch
// TIME_LIMIT seconds have passed since the last batch of connections
/* Use cURL multi to make 10 asynchronous connections to the API */
// Once all of those connections are made and processed, save the current time:
$tLast = timestamp();
}else{
// TIME_LIMIT seconds have not yet passed
// Calculate the total number of seconds remaining until TIME_LIMIT seconds have passed:
$timeDifference = $tLast + TIME_LIMIT - timestamp();
sleep( $timeDifference ); // Sleep for the calculated number of seconds
}
} // END WHILE-LOOP
/* Do any additional processing, computing, and output */
?>
Заметка: В этом фрагменте кода я также использую ignore_user_abort()
функция. Как отмечено в комментарии к коду, эта функция просто позволяет сценарию игнорировать прерывание пользователя, поэтому, если пользователь закрывает браузер (или подключение) во время выполнения сценария, сценарий продолжит извлекать и обрабатывать данные из API в любом случае. Возможно, вы захотите отключить это в своей реализации, но я оставлю это на ваше усмотрение.
Очевидно, что этот код очень неполный, но он должен дать вам приличное понимание того, как вы могли бы реализовать решение этой проблемы.
Не замедляйте индивидуальные запросы.
Вместо этого вы обычно используете что-то вроде Redis для отслеживания запросов по IP или по каждому пользователю. Как только ограничение достигнет определенного периода времени, отклоните его (возможно, с кодом состояния HTTP 429), пока счетчик не будет сброшен.
http://redis.io/commands/INCR в сочетании с http://redis.io/commands/expire легко бы сделать свое дело.