ОТЧЕТ О ДОХОДНОСТИ
14855
ВКЛАД, $
14855
ИТОГ, $
0
П/У, $
0
П/У, %
1506,81
ВКЛАД, $
1524,54
ИТОГ, $
17,73
П/У, $
1,176
П/У, %
29027
ВКЛАД, $
29027
ИТОГ, $
0
П/У, $
0
П/У, %
1014,88
ВКЛАД, $
1015,73
ИТОГ, $
0,85
П/У, $
0,08
П/У, %
2820,8
ВКЛАД, $
2840,98
ИТОГ, $
20,18
П/У, $
0,715
П/У, %
281,25
ВКЛАД, $
280,93
ИТОГ, $
- 0,32
П/У, $
- 0,11
П/У, %

Простой планировщик задач на PHP

В процессе эволюции более-менее крупного проекта может настать ситуация, когда количество запланированных задач (cron jobs) становится настолько большим, что поддержка их становится ночным кошмаром devops'ов. Для решения этой проблемы мне пришла в голову идея создать реализацию планировщика на PHP, который можно изучить на webshake: https://webshake.ru/php-training-course, тем самым сделав его частью проекта, а сами задачи — частью его конфигурации. В этом случае необходимое и достаточное количество cron jobs будет равно единице.


Некоторое время назад мне довелось разрабатывать модуль для планирования событий. Некое упрощенное подобие Google/Apple Calendar для пользователей приложения. Для хранения дат и правил повторения событий было решено использовать формат iCalendar (RFC 5545), позволяющий одной строкой описать график повторения какого-либо события с учетом дней недели, месяцев, количества повторений и многого другого. Несколько примеров:


FREQ=WEEKLY;BYDAY=SU,WE — Еженедельно в субботу и среду
FREQ=MONTHLY;COUNT=5 — Каждый месяц, пять раз
FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU — Каждый второй год в каждую субботу января


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


Для работы с форматом iCalendar была найдена замечательная библиотека (не пожалейте звезду):
https://github.com/simshaun/recurr


Имея инструмент для работы с RRULE (Recurrence Rule) дело осталось за малым. Написать несколько классов, позволяющих планировать и запускать задачи (являющиеся каким угодно проявлением PHP callable типа).


Установка библиотеки:

composer require hutnikau/job-scheduler


Планирование и запуск задач:

SchedulerJobJob — Класс, представляющий задачу


Для создания его экземпляра потребуется правило его повторения (RRULE) и экземпляр типа callable:


$startTime = new DateTime('2017-12-12 20:00:00');
$rule = new SchedulerJobRRule('FREQ=MONTHLY;COUNT=5', $startTime); //run monthly, at 20:00:00 starting from the 12th of December 2017, 5 times
$job = new SchedulerJobJob($rule, function () {
   //do something
});

Альтернативный вариант — использовать SchedulerJobJob::createFromString():


$job = SchedulerJobJob::createFromString(
   'FREQ=MONTHLY;COUNT=5', //Recurrence rule
   '2017-12-28T21:00:00',  //Start date
   function() {},          //Callback
   'Europe/Minsk'          //Timezone. If $timezone is omitted, the current timezone will be used
);

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


Добавляем задачу в планировщик:


$scheduler = new SchedulerScheduler()
$scheduler->addJob($job);

Так же можно передать массив задач в конструктор:


$scheduler = new SchedulerScheduler([
   $job,
   //more jobs here
])

Запускаем запланированные задачи:


$jobRunner = new SchedulerJobRunnerJobRunner();
$from      = new DateTime('2017-12-12 20:00:00');
$to        = new DateTime('2017-12-12 20:10:00');
$reports   = $jobRunner->run($scheduler, $from, $to, true);

В данном примере будут выполнены все задачи, запланированные на указанный промежуток времени (10 минут). Таким образом вам потребуется всего один cron job, запускающий JobRunner.


Можно опустить параметр $to, таким образом будут выполнены все задачи, начиная от $from до текущего момента.


Последний параметр определяет, будут ли выполнены задачи, время выполнения которых попало точно на пограничные значения ('2017-12-12 20:00:00' и '2017-12-12 20:10:00' из примера выше).


При запуске планировщика при помощи cron я советую сохранять время последнего запуска, и при следующем запуске передавать его в параметр $from прибавив одну секунду, так как точность cron'а не идеальна, и существует вероятность пропустить какие-либо задачи или выполнить их дважды.


$jobRunner->run(...)возвращает массив результатов выполненных задач (массив объектов типа SchedulerActionReport).


SchedulerActionReport {
   /* Methods */
   public mixed getReport ( void )
   public Action getAction ( void )
   public string getType ( void )
}

Вызвав SchedulerActionReport::getReport() можно получить результат выполнения callable (возвращенное им значение).


В случае, если при выполнении задачи было брошено исключение, SchedulerActionReport::getReport() вернет то самое исключение.


Метод SchedulerActionReport::getAction() вернет экземпляр типа SchedulerActionActionInterface, который описывает выполненное действие. Используя его можно узнать время выполнения действия или получить само действие (Job).


Так же стоит обратить внимание, что если запланированная задача должна была выполниться более одного раза (например если в RRULE был использован интервал MINUTELY, и разница между $from и $to, переданным в JobRunner 10 минут), то действие будет выполнено несколько раз. Другими словами они не будут сгруппированы.


Вот, пожалуй, и все. Библиотека действительно мала, но надеюсь окажется кому-либо полезной.
Конструктивная критика и помощь в развитии приветствуются.

Pasprofit - новый уровень деятельности

Мы открываем компанию "PasProfit", которая будет заниматься финансовым консалтингом подробнее читать о нас

КАЖДЫЙ ДЕНЬ В ВАШЕМ ПОЧТОВОМ ЯЩИКЕ

Ежедневно получайте авторскую подборку материалов на Ваш E-mail

Ваш email:
Интервью
Все интервью
Интервью с СЕО PrimeBroker.pro
Дмитрий Леушкин
СЕО PrimeBroker.pro
16.10.2016

Нам удалось получить ответы на появившееся вопросы, связанные с подробностями развития нового брокера.

Пресс-релизы
Все пресс-релизы
Паспрофит – в новый год, к новым горизонтам!
07.01.2016

За несколько месяцев компания «Паспрофит» проделала огромную работу, сумела расширить поле действия и доказать свою состоятельность на нескольких фронтах. Сегодня потенциал компании уже очевиден, но к дню сегодняшнему мы шли длительное время и хотели бы отчитаться перед нашими клиентами, соратниками и постоянными читателями!