Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
мне кажется ты просто от очередей хочешь странного, они предназначены для того чтобы просто в фоне выполнять задачи, а не для какой-то хитрой логики с ограничением количества выполняемых задач по времени. мне кажется что требуемый функционал специфичен для конкретных типов задач и не должен применяться глобально на уровне целой очереди
обрати внимание на трейт Illuminate\Queue\InteractsWithQueue – он в установке по умолчанию уже присутствует во всех задачах, у него есть метод release() который позволяет на произвольное количество секунд отложить выполнение задачи. ты можешь проверять время последнего обращения к API и релизить задачу на некое случайное количество секунд, если с последнего обращения прошло недостаточно времени. сначала все задачи будут релизиться но достаточно быстро раскидаются по очереди из-за рандома. естественно порядок их выполнения будет тоже случаен, но частота обращения к API не будет превышать требуемую, даже если очередь будет параллельно обрабатываться несколькими процессами-воркерами
ещё один плюс такого решения в том что ты сможешь использовать очередь параллельно для других задач. если задача делает sleep() на несколько секунд – она блокирует очередь. релизнутые задачи очередь не блокируют и она может использоваться другими задачами свободно. если хранить счётчик времени последнего обращения к API где-нибудь в мемкэше или редисе, оно ещё и не будет нагружать систему в процессе его опроса
Насчет специфичности и хитрой логики - могу поспорить, т.к. очередь чаще всего как раз и используется для того, чтобы:
а) не выполнять задачи в фоне, пока юзер ждет ответ
б) не делать кучу действий за раз и выполнять их последовательно (именно в этом варианте я и ожидаю использование задержек, т.к. ранее работал с такими же задачами, но в других экосистемах (python))
Про release() - спасибо, думал о нем, но смущает то, что я не понимаю как задача будет возвращаться в очередь и будет ли это считаться за попытку выполнения (attempt), т.к. она может уходить из очереди допустим, каждый раз, а выполниться ей придется через 10 запусков.
Из-за такого можно словить "пропажу" задач из очереди, а делать attempts=999999 тоже нет никакого желания...
Пока что буду использовать первый вариант и запускать queue:work через shedule:run, но меня не устраивает данный метод, т.к. будут постоянные перезапуски команды, что не круто, т.к. рано или поздно это начнет забивать память (GC где-то не отработает), плюс к этому мне нужно запускать 50 задач (Job) в сутки, а придется запускать queue:work постоянно, чтобы он не пропустил задачи.
Поясню дополнительно почему не нравится этот вариант: очередь - это очередь, а не запуск команды по cron-задаче. Хочется, чтобы висел какой-то один процесс и постоянно слушал новые задачи для себя. Если делать так, как я поступил сейчас, то вообщем-то и очередь не нужна в том понимании, котором она есть. Просто запускать по крону задачи и всё. Это не красивый вариант, который хочется поменять. Поэтому третий описанный мной способ - более интересен для реализации у себя.
Придумал несколько способов решения свой проблемы:
1. Использовать параметр --once для queue:work, чтобы запускать по 1 задаче в запуск команды.
2. Сделать дополнительное поле runned_at в модели, где лежат API-ключи и перезапускать задачу, если нет ключей, удовлетворяющих условию runned_at < (текущее время - 1 минута).
3. Пронаследовать queue:work и переопределить получение задач: сделать, чтоб он получал задачи не пачкой, а по одной и между получением следующей задачи выполнял задержку в delay секунд
Пока что буду использовать первый вариант и запускать queue:work через shedule:run, но меня не устраивает данный метод, т.к. будут постоянные перезапуски команды, что не круто, т.к. рано или поздно это начнет забивать память (GC где-то не отработает), плюс к этому мне нужно запускать 50 задач (Job) в сутки, а придется запускать queue:work постоянно, чтобы он не пропустил задачи.
Поэтому ваши предложения приветствуются. Если вы считаете, что третий вариант самый "православный" - объясните, пожалуйста, от чего пронаследоваться, а то я бегло поискав эту команду в дебрях лары - не понял от чего пронаследоваться и где переопределять.
Нашел на ларакастс похожий трабл, народ там совсем не думает головой и предлагает делать sleep или добавлять счетчик, который будет использоваться в параметре метода ->delay().
Я не готов к таким костылям, т.к. в первом случае со sleep - может выйти ограничение по времени, если обрабатывать все GetAPIItem в одной задаче, а делать что-то вроде --timout 99999999 я считаю идиотизмом.
А во втором случае с delay опять же возникнет проблема, что задачи могут пересечься и в итоге одна из них "сфейлится".
Перечитал русскую документацию о параметре sleep и удивился, что я не правильно понял его в англоязычной документации.
Почему реализовано так, что перед каждой задачей он не применяется?
Как мне это обойти и запускать задачи из одной очереди с какой-либо задержкой?
Была мысль запускать ежеминутно через schedule команду queue:work, но она почему-то запускает сразу все имеющиеся задачи, а не одну (как написано в документации).
И вопрос из PPS про фейлинг задачи всё ещё не решен...
Версия Laravel: v5.4.16
Версия PHP: 7.1.3
Операционная система и её версия: macOS 10.12.4
Вендор и версия сервера БД: mysql Ver 14.14 Distrib 5.7.17, for osx10.12
Вендор и версия Веб-сервера: nginx/1.10.3
Медод подключения PHP: Valet
Недавно начал изучать Laravel, поставил через Valet последнюю версию.
Пилю систему, которая работает со сторонним API, которое ограничивает количество запросов, поэтому сразу же начал писать используя очередь. Но столкнулся с неприятной особенностью/багом - при запуске:
php artisan queue:work --delay=60 --tries=3 --sleep=5
Все задачи выполняются подряд и без всяких задержек:
[2017-04-04 04:04:43] Processed: App\Jobs\GetAPIData
[2017-04-04 04:04:43] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:44] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:44] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:45] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:45] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:46] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:46] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:46] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:47] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:47] Processed: App\Jobs\GetAPIItem
[2017-04-04 04:04:48] Processed: App\Jobs\GetAPIItem
...
Здесь первая задача GetAPIData получает данные от провайдера и добавляет в очередь ещё кучу задач GetAPIItem. Первую задачу запускаю через schedule, но это вообщем-то не играет особой роли. Она просто попадает в очередь, а потом уже я запускаю команду для запуска очереди.
К слову, использую драйвер database, не sync. Проверял в tinker через env().
Вначале пытался сделать это через задание определенной очереди, запускал с соответствующим аргументом и получал тоже самое.
Пытался прописывать одноименные параметры в классах задач - результат тот же (если, конечно, правильно прописывал).
Я что-то делаю не так или не понимаю логику задержек?
PS: пожалуйста, не советуйте использовать ->delay(время) - это отвратительный метод. У меня несколько задач будет, которые работают с API и там могут возникнуть ситуации, что они пересекутся, а отлавливать эти моменты ой как не просто будет.
PPS: ещё заметил, что если задача зафейлилась, то она не возвращается обратно, хотя установленое количество попыток - 3.