Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 04.04.2017 07:12:54

Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Недавно начал изучать 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.

Изменено Lord_Alfred (04.04.2017 07:19:08)

Не в сети

#2 04.04.2017 07:23:39

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Версия 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

Изменено Lord_Alfred (04.04.2017 07:24:55)

Не в сети

#3 04.04.2017 07:34:39

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Перечитал русскую документацию о параметре sleep и удивился, что я не правильно понял его в англоязычной документации.

Почему реализовано так, что перед каждой задачей он не применяется?

Как мне это обойти и запускать задачи из одной очереди с какой-либо задержкой?

Была мысль запускать ежеминутно через schedule команду queue:work, но она почему-то запускает сразу все имеющиеся задачи, а не одну (как написано в документации).

И вопрос из PPS про фейлинг задачи всё ещё не решен...

Изменено Lord_Alfred (04.04.2017 07:35:04)

Не в сети

#4 04.04.2017 07:44:09

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Нашел на ларакастс похожий трабл, народ там совсем не думает головой и предлагает делать sleep или добавлять счетчик, который будет использоваться в параметре метода ->delay().
Я не готов к таким костылям, т.к. в первом случае со sleep - может выйти ограничение по времени, если обрабатывать все GetAPIItem в одной задаче, а делать что-то вроде --timout 99999999 я считаю идиотизмом.
А во втором случае с delay опять же возникнет проблема, что задачи могут пересечься и в итоге одна из них "сфейлится".

Не в сети

#5 04.04.2017 08:47:06

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Придумал несколько способов решения свой проблемы:


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 постоянно, чтобы он не пропустил задачи.

Поэтому ваши предложения приветствуются. Если вы считаете, что третий вариант самый "православный" - объясните, пожалуйста, от чего пронаследоваться, а то я бегло поискав эту команду в дебрях лары - не понял от чего пронаследоваться и где переопределять.

Не в сети

#6 04.04.2017 09:09:50

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Lord_Alfred пишет:

Пока что буду использовать первый вариант и запускать queue:work через shedule:run, но меня не устраивает данный метод, т.к. будут постоянные перезапуски команды, что не круто, т.к. рано или поздно это начнет забивать память (GC где-то не отработает), плюс к этому мне нужно запускать 50 задач (Job) в сутки, а придется запускать queue:work постоянно, чтобы он не пропустил задачи.

Поясню дополнительно почему не нравится этот вариант: очередь - это очередь, а не запуск команды по cron-задаче. Хочется, чтобы висел какой-то один процесс и постоянно слушал новые задачи для себя. Если делать так, как я поступил сейчас, то вообщем-то и очередь не нужна в том понимании, котором она есть. Просто запускать по крону задачи и всё. Это не красивый вариант, который хочется поменять. Поэтому третий описанный мной способ - более интересен для реализации у себя.

Не в сети

#7 04.04.2017 09:39:41

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

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

обрати внимание на трейт Illuminate\Queue\InteractsWithQueue – он в установке по умолчанию уже присутствует во всех задачах, у него есть метод release() который позволяет на произвольное количество секунд отложить выполнение задачи. ты можешь проверять время последнего обращения к API и релизить задачу на некое случайное количество секунд, если с последнего обращения прошло недостаточно времени. сначала все задачи будут релизиться но достаточно быстро раскидаются по очереди из-за рандома. естественно порядок их выполнения будет тоже случаен, но частота обращения к API не будет превышать требуемую, даже если очередь будет параллельно обрабатываться несколькими процессами-воркерами

ещё один плюс такого решения в том что ты сможешь использовать очередь параллельно для других задач. если задача делает sleep() на несколько секунд – она блокирует очередь. релизнутые задачи очередь не блокируют и она может использоваться другими задачами свободно. если хранить счётчик времени последнего обращения к API где-нибудь в мемкэше или редисе, оно ещё и не будет нагружать систему в процессе его опроса

Не в сети

#8 04.04.2017 09:44:28

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

ещё один вариант кстати, воспользоваться тем что кроме default-очереди в приложении могут быть и другие. если у тебя есть отдельный воркер, обрабатывающий default очередь для обычных задач и отдельный apirequests-воркер для опроса API, ты можешь в нём спокойно делать sleep() перед отправкой запроса и блокировать эту очередь как хочешь – это отдельная песочница для отдельных задач и на работу основной очереди она влиять не будет. может это не очень красивое решение, но я не вижу никакой проблемы

Не в сети

#9 04.04.2017 09:47:17

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

ещё кстати паузу между задачами очень просто сделать, если вспомнить что очередь при работе генерирует события, в частности Illuminate\Queue\Events\JobProcessing перед выполнением задачи и Illuminate\Queue\Events\JobProcessed – после. если задать обработчик и в нём вызвать sleep() – ты и получишь задержку перед обработкой следующей задачи

если решишь так делать – настоятельно советую вынести задачи по апи в очередь с отдельным именем и в «засыпающем» обработчике проверять $event->connectionName, чтобы не тормозить работу вообще всех очередей без разбору

Не в сети

#10 04.04.2017 09:47:22

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

constb пишет:

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

обрати внимание на трейт Illuminate\Queue\InteractsWithQueue – он в установке по умолчанию уже присутствует во всех задачах, у него есть метод release() который позволяет на произвольное количество секунд отложить выполнение задачи. ты можешь проверять время последнего обращения к API и релизить задачу на некое случайное количество секунд, если с последнего обращения прошло недостаточно времени. сначала все задачи будут релизиться но достаточно быстро раскидаются по очереди из-за рандома. естественно порядок их выполнения будет тоже случаен, но частота обращения к API не будет превышать требуемую, даже если очередь будет параллельно обрабатываться несколькими процессами-воркерами

ещё один плюс такого решения в том что ты сможешь использовать очередь параллельно для других задач. если задача делает sleep() на несколько секунд – она блокирует очередь. релизнутые задачи очередь не блокируют и она может использоваться другими задачами свободно. если хранить счётчик времени последнего обращения к API где-нибудь в мемкэше или редисе, оно ещё и не будет нагружать систему в процессе его опроса

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

Про release() - спасибо, думал о нем, но смущает то, что я не понимаю как задача будет возвращаться в очередь и будет ли это считаться за попытку выполнения (attempt), т.к. она может уходить из очереди допустим, каждый раз, а выполниться ей придется через 10 запусков.
Из-за такого можно словить "пропажу" задач из очереди, а делать attempts=999999 тоже нет никакого желания...

Не в сети

#11 04.04.2017 09:50:21

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

constb пишет:

ещё кстати паузу между задачами очень просто сделать, если вспомнить что очередь при работе генерирует события, в частности Illuminate\Queue\Events\JobProcessing перед выполнением задачи и Illuminate\Queue\Events\JobProcessed – после. если задать обработчик и в нём вызвать sleep() – ты и получишь задержку перед обработкой следующей задачи

если решишь так делать – настоятельно советую вынести задачи по апи в очередь с отдельным именем и в «засыпающем» обработчике проверять $event->connectionName, чтобы не тормозить работу вообще всех очередей без разбору

Спасибо, идея интересная (хоть и чуть костыльная) - как раз задачи по API будут в отдельной очереди лежать. Но я не понял про засыпающий обработчик и коннект - он может отвалиться или что может пойти не так?

Изменено Lord_Alfred (04.04.2017 09:50:38)

Не в сети

#12 04.04.2017 09:51:36

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

нет, release() не увеличивает количество попыток. он просто возвращает задачу в очередь и откладывает её выполнение на указанное количество секунд

насчёт «не делать кучу действий за раз» – как раз наоборот, очереди спроектированы так чтобы можно было легко распараллеливать выполнение задач на множество процессов, разбивая их на отдельные логические «кубики»

Не в сети

#13 04.04.2017 09:53:48

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

constb пишет:

нет, release() не увеличивает количество попыток. он просто возвращает задачу в очередь и откладывает её выполнение на указанное количество секунд

Понял, спасибо.
Если после того как посплю - не придет озарения, то скорее всего так и перепишу работу, а то запуск через "планировщик" по расписанию меня очень сильно смущает тем, что пропадает "слушатель" и весь смысл очередей

Не в сети

#14 04.04.2017 09:54:13

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

Но я не понял про засыпающий обработчик и коннект - он может отвалиться или что может пойти не так?

нет, «засыпающим» я назвал обработчик события который делает sleep(). $event->connectionName – это строка, которая содержит идентификатор очереди, default для основной и что хочешь для отдельных. соответственно если $event->connectionName === 'api_requests' – тогда спим иначе нет. вот я про что. загляни в Illuminate\Queue\Events\JobProcessed – он тебе и придёт в качестве $event из воркера

Не в сети

#15 04.04.2017 21:46:11

Re: Отсутствует задержка при выполнении задачи (Job) из очереди (Queue)

constb пишет:

нет, release() не увеличивает количество попыток. он просто возвращает задачу в очередь и откладывает её выполнение на указанное количество секунд

насчёт «не делать кучу действий за раз» – как раз наоборот, очереди спроектированы так чтобы можно было легко распараллеливать выполнение задач на множество процессов, разбивая их на отдельные логические «кубики»

Переписал на этот вариант и проверил - release() - увеличивает количество попыток

Не в сети

Подвал раздела