Попытки оптимизации и автоматизации работы вашего сайта на движке WordPress могут вас привести к тому что нужно будет перевести часть «тяжёлого» функционала в «фон», что бы не обременять ваших посетителей ожиданиями отклика сайта или выполнения каких либо действий на странице. Собственно профессиональные и высоко нагруженные проекты используются для таких случаев серверы очередей (RabbitMQ и д.р).Но если хочется более простой реализации «очередей» и у себя на хостинге — можно попробовать достаточно интересное решение которое эмулирует очередь на WordPress при помощи крон. При этом постановка в очередь, прослушивание очереди и отработка задания в очереди происходит по аналогии с другими более крупными решениями реализаций очередей.
И так, для чего может пригодится очередь заданий (выдуманные примеры взятые для понимания):
1. Добавление комментария с проверкой его на спам + отправка уведомления + другие операции
После нажатия на кнопку отправить комментарий. Ваш WordPress по мимо его записи в базу может делать:
- Запрос через сеть к сервисам по проверки комментария на спам (сетевые операции могут занимать ощутимое время выполнения)
- Запрос к smtp для отправки сообщения ( в этом случае будет уходить время на соединение и авторизацию на smtp сервере)
- Запрос к телеграм боту, который вас уведомит о новом событии у вас на сайте (так же это сетевой запрос)
И пока вся эта цепочка выполняется — ваш посетитель ждёт, пока его комментарий отправится.
2. Обработка прайса или файла который содержит информацию о товарах для вашего WooCommerce
3. Какие либо сервисные операции или одноразовые задачи
Ниже небольшой класс для организации очередей
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
<?php if (!defined('ABSPATH')) { exit; } /** * Очереди на WP */ class WP_Queue { /** * @var array очереди */ private $queue = []; /** * @var array подписчики */ private static $subscriptions = []; private static $queue_prifix = 'coderun_wp_queue_'; /** * . * * Создает объект очереди для удаления из очереди. Используйте статические функции для постановки заданий в очередь. */ public function __construct() { add_action('all', [$this, 'ready_queue'], 2); register_shutdown_function([$this, 'dequeue']); //Срабатывает по завершению работы скрипта } /** * Помещает данные в очередь * * @param string $topic Индификатор очереди * @param mixed $data Данные отправляемые в очередь(полезная нагрузка) * @param int $delay время отсрочки для выполнения задания в секундах */ public static function enqueue(string $topic, $data, int $delay = 0) { wp_schedule_single_event(time() + $delay, self::$queue_prifix . sha1($topic . '_' . microtime()), [ [ 'data' => $data, 'topic' => $topic, ] ]); } /** * Обработчик задания очереди * * @param string $topic Индификатор очереди за которым нужно следить * @param callable $callback Функция которая будет выполнена, принимает аргумент $data из self::enqueue */ public static function subscribe(string $topic, callable $callback) { if (!isset($subscriptions[$topic])) { self::$subscriptions[$topic] = []; } self::$subscriptions[$topic][] = $callback; } /** * Очистка очереди */ public function dequeue() { foreach ($this->queue as $topic => &$queue) { foreach ($queue as &$data) { if (isset(self::$subscriptions[$topic])) { foreach (self::$subscriptions[$topic] as &$callback) { if (is_callable($callback)) { try { $callback($data['data']); } catch (Throwable $exception) { $data['exception'] = [ 'type' => 'Throwable', 'exception' => $exception, ]; $data['handler'] = $callback; self::enqueue('dead-letter', $data); } catch (Exception $exception) { $data['exception'] = [ 'type' => 'Exception', 'exception' => $exception, ]; $data['handler'] = $callback; self::enqueue('dead-letter', $data); } } } } } } } /** * Обработка событий WP * * @param string $tag * @param array $data */ public function ready_queue($tag = false, $data = false) { if (empty($data) || empty($tag)) { return false; } if (strpos($tag, self::$queue_prifix) !== false) { $data = &$data[0]; if (!is_array($this->queue[$data['topic']])) { $this->queue[$data['topic']] = []; } $this->queue[$data['topic']][] = &$data; } } } |
Пример добавления задания в очередь:
1 2 3 4 |
//Отправить в очередь WP_Queue::enqueue('jora_gde_pivo', ['ONE' => 'JORA', 'POPA' => 'dddd1123'], 120); |
При добавлении в очередь — первый параметр это название очереди который мы потом будет ловить в обработчике.
Далее идёт массив параметров, в массив параметров вы добавляете всё что вам может потребоваться в обработчике очереди
Последний параметр — это время в секундах через которое будет проверенно это задание.
Пример того как забрать задание из очереди и выполнить его:
1 2 3 4 5 6 7 8 |
if (defined('DOING_CRON')) { new WP_Queue(); WP_Queue::subscribe('jora_gde_pivo', function($params) { wp_mail('vv@putin.ru', 'test', 'hello cron queque WORDPRESS - ' . implode('::', $params)); }); } |
Обработка очереди начинается с создание объекта класса.
Затем запускаем обработчик подписки на очередь в который нужно передать два параметра:
1. Имя очереди, этот параметры мы обозначили при добавлении в очередь
2. Функция которая принимает массив параметров и выполняет каких либо действий. В примере это отправка сообщения на почту.
При автоматизации каких либо действий на сайте вы можете создавать сколько угодно новых очередей с разными именами и вешать на них сколько угодно подписчиков.
Технически это выглядит так:
У вас на сайте создаётся одноразовое крон задание при создании очереди, которое будет выполнено через указанное время секунд. Дальше это задание протухает. Если вам нужно повторно запустить это задание(например вы обрабатывает огромный csv файл) — тогда в обработчике очереди по завершению каких-либо действий создайте новое задание с тем же именем и параметрами.
Особенность: обработка очереди должна происходить только в момент работы cron — т.е конда константа DOING_CRON будет true, а это происходит при запуске файла wp-cron.php
Приведённые примеры являются лишь примерами. Класс и его реализацию вы можете использовать по вашим требованиям. Делитесь вашими заметками в комментариях к записи.
Класс оригинал https://github.com/withinboredom/wp-queue