Как стать автором
Обновить

Пример использования Workerman и Symfony Messenger

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров1.4K
Результаты работы примера совместного использования Workerman и Symfony Messenger.
Результаты работы примера совместного использования Workerman и Symfony Messenger.

Предыстория

В процессе изучения работы PHP компонента Symfony Messenger (https://symfony.com/doc/current/components/messenger.html) мной был создан самодостаточный пример совместной работы Symfony Messenger и Symfony Console, подробно описанный в статье https://habr.com/ru/articles/817425/.

Для демонстрации работы этого примера нужно было вручную запустить несколько консолей (терминалов), а потом в каждой вручную запустить Worker.
Мой внутренний перфекционист :-) сильно против этого возражал и говорил «а вот бы все эти консоли-терминалы запускались одной командой, в нужном количестве, сразу с Worker’ами, а если какой Worker упадёт, то заново запускались в нужном количестве».

Возражать своему внутреннему перфекционисту я не стал и создал ещё один пример работы Symfony Messenger, который запускается Worker’ами из PHP фреймворка Workerman (https://github.com/walkor/workerman). При этом Symfony Console вообще не используется.

Подробнее о Workerman можно узнать здесь: https://manual.workerman.net/doc/ru/ (описание на русском языке).

Сам пример использования Symfony Messenger и Workerman

Как и в прошлом примере, очередь сообщений хранится в используемой через Doctrine базе данных SQLite.

Сам пример можно взять отсюда:
https://github.com/balpom/symfony-messenger-and-workerman

Либо можно установить через Composer:
composer create balpom/symfony-messenger-and-workerman

Как запустить пример

После установки откройте консоль и перейдите в созданную Composer'ом директорию symfony-messenger-and-workerman.
Выполните команду:
php bin/start
Эта команда запустит три простых Worker’а, имитирующих отправку SMS. Сейчас они ждут, когда в очереди появятся сообщения.
Количество Worker’ов настраивается в файле bin/runner.

Выполните команду:
php tests/sendmany.php
Эта команда запустит простой скрипт, который добавит в очередь несколько десятков сообщений.
После этого в ранее открытых консолях можно увидеть, как Worker'ы совместно "отправляют" SMS, берущиеся ими из очереди.

Выполните команду:
php bin/reload
Все Worker’ы "доотправят" взятые ими в работу SMS, завершат работу, запустятся заново и продолжат "отправлять" SMS (если они есть в очереди).

Выполните команду:
php bin/stop
Все Worker’ы "доотправят" взятые ими в работу SMS и завершат работу.

Тонкости реализации и выявленные недостатки

Призрак Symfony Console

Да, от компонента Symfony Console мы избавились. Однако его бледная тень в нашем примере незримо присутствует.
Дело в том, что класс SymfonyWorker, являющийся некой обёрткой для класса Symfony\Component\Messenger\Worker, примерно наполовину сделан на основе метода execute класса ConsumeMessagesCommand (Symfony\Component\Messenger\Command\ConsumeMessagesCommand).
Ну да, ничего умнее придумать не смог… ;-)

"Запускатор" для Workerman\Worker

Скрипт, запускающий аж целый asynchronous event-driven PHP framework with high performance, прост до безобразия и я позволю себе привести его здесь почти целиком (чуть позже мне это будет нужно ещё и для более простого описания выявленных недостатков).

 // bin/runner
namespace Balpom\SymfonyMessengerWorkerman;
use Workerman\Worker;
use Symfony\Component\Process\Process;
Worker::$daemonize = true; // Always run as daemon.
$worker = new Worker();
$worker->count = 3;        // Numbef of Workers.
// SymfonyWorkerFactory::getWorker(DIR . '/../config/dependencies.php')->run();
$process = new Process(['gnome-terminal', '--', 'php', 'bin/start_worker']);
$process->run();
};
Worker::runAll();

Прям "из коробки" эта конструкция понимает говорящие сами за себя команды
php bin/runner start
php bin/runner reload
php bin/runner stop

и некоторые другие (см. https://manual.workerman.net/doc/ru/install/start-and-stop.html).

Чтобы при запуске Workerman в режиме демона у команды "start" не указывать опцию "-d", в скрипте прописано Worker::$daemonize = true.

Работа команд php bin/runner start, php bin/runner reload и php bin/runner status. В "Системном мониторе" видны соответствующие PHP-процессы.
Работа команд php bin/runner start, php bin/runner reload и php bin/runner status.
В "Системном мониторе" видны соответствующие PHP-процессы.
Работа команды php bin/runner stop. В "Системном мониторе" не видно ни одного PHP-процесса. Команда php bin/runner status после остановки Workerman'а ничего не выводит.
Работа команды php bin/runner stop.
В "Системном мониторе" не видно ни одного PHP-процесса.
Команда php bin/runner status после остановки Workerman'а ничего не выводит.

Запуск Worker’ов "под микроскопом"

Если вы чуть более внимательно посмотрите на вышеприведённый код "запускатора", то увидите, что в своём примере Worker’ы, которые Worker’ы Symfony Messenger, а не Workerman’а, ;-) запускаются из Gnome Terminal.
Соответственно, если он у вас не установлен, то данный пример вам придётся адаптировать под ваши реалии.
Да, запускать всё это под Windows я не пробовал. Чёрт его знает, может, и будет работать, если как-то на запуск через команду "start" переделать...

Файл "bin/start_worker" запускает Symfony Worker и выглядит так:
namespace Balpom\SymfonyMessengerWorkerman;
SymfonyWorkerFactory::getWorker('/../config/dependencies.php')->run();

Файл "bin/stop_worker" останавливает Symfony Worker и выглядит так:
namespace Balpom\SymfonyMessengerWorkerman;
SymfonyWorkerFactory::getWorker('/../config/dependencies.php')->stopWorkers();

Файлы "bin/start" и "bin/stop" — это некий синтаксический сахар (чтобы поменьше символов в консоли набирать ;-) ) и выглядят они так:
// bin/start
use Symfony\Component\Process\Process;
$process = new Process(['php', 'bin/runner', 'start']);
$process→run();

// bin/stop
$process = new Process(['php', 'bin/stop_workers']);
$process->run();
$process = new Process(['php', 'bin/runner', 'stop']);
$process->run();

Ну хорошо, хорошо… "bin/stop" - не совсем синтаксический сахар…
Как видно, он отдельно даёт команду на остановку Worker’ов Symfony Messenger, а потом уже Worker’ов Workerman’а.
Ну да, ничего умнее не придумал… ;-)

Файл "bin/reload" даёт команду на остановку Worker’ов Symfony и Worker’ов Workerman’а, а потом заново запускает workerman:
// bin/reload
$process = new Process(['php', 'bin/stop']);
$process->run();
$process = new Process(['php', 'bin/runner', 'start']);
$process->run();

Worker’ы Symfony, запущенные внутри терминалов, работают не так, как ожидалось

Да, конечно, возможность наблюдать работу Worker’ов в окнах терминалов — это наглядно и позволяет контролировать весь процесс.
Однако при тестировании этого всего столкнулся с тем, что если по каким-то причинам (не важно по каким — может, по таймауту / по числу обработанных message’s да или просто по kill) Worker Symfony Messenger прекратит свою работу, то новые консоли с Worker’ами Symfony не открываются.
При этом в «Системном мониторе» видно, что соответствующие PHP-процессы Worker’ов Workerman’а вполне себе живы-здоровы и умирать не собираются (если б умерли — то были бы автоматически перезагружены Workerman’ом и консоли бы открылись).
Как-то "правильно" запускать Workerman’ом из терминала Worker’ы Symfony у меня так и не получилось… :-(

Так как в реальных задачах вряд ли есть необходимость запускать Worker’ы именно в терминалах, то я не сильно-то и расстроился из-за вышеописанного непонятного поведения Worker’ов.
Тем более, что при запуске Worker’ов Symfony напрямую (закомментированная строка в коде "запускатора") они вполне себе работают как положено (ну да, работу по "отправке" не видно, ну да, можно было вывод не в консоль, а в файл выводить, но мне лень стало дальше этот пример усложнять пилить ;-) ).

Послесловие

Используя Workerman (https://manual.workerman.net/doc/ru/), я смог как нефиг делать создать простого демона простым и понятным образом.

Я осведомлён о существовании AMPHP (https://amphp.org/), ReactPHP (https://reactphp.org/) и Swoole (https://openswoole.com/).
Однако все они показались мне слишком замудрёнными и требующими какого-то прям длительного и глубокого изучения их возможностей прежде чем что-то осмысленно с их помощью делать.
Да и как-то это, наверное, перебор — использовать таких тяжеловесов лишь для того, чтобы с нуля не создавать простого демона. :-)

Хотя… ;-)

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 3: ↑3 и ↓0+4
Комментарии4

Публикации

Работа

Ближайшие события