Пример своего транспорта для Symfony Messenger

Сравнительное тестирование транспорта сообщений для Symfony Messenger.

Сравнительное тестирование транспорта сообщений для Symfony Messenger.

Предыстория

В процессе изучения Symfony Messenger мной было создано два самодостаточных примера, демонстрирующих его работу:
https://habr.com/ru/articles/817425/ — Symfony Messenger и Symfony Console
https://habr.com/ru/articles/819187/ — Symfony Messenger и Workerman

В каждом из этих учебных примеров в качестве транспорта сообщений для простоты была выбрана БД SQLite (для понимания работы Symfony Messenger мне совершенно не хотелось возиться с установкой Redis, RabbitMQ или Mongo).
Готовой реализации транспорта именно для SQLite я не нашёл и пришлось её использовать через DBAL Doctrine (https://github.com/symfony/doctrine-messenger).

И всё бы ничего, но внутренний перфекционист :-) нашёптывал, что использование целой Доктрины лишь для того, чтобы работать с одной-единственной таблицей с очередями сообщений — это явный перебор…

Бороться с затерроризировавшим меня внутренним перфекционистом ;-) я не стал и, решив поглубже разобраться с устройством транспорта сообщений в Symfony Messenger, создал такой транспорт для SQLite сам, с использованием PDO.
Также было произведено тестирование производительности этого моего решения и решения на Doctrine с БД на жёстком диске и на RAM диске.

SQLite транспорт для Symfony Messenger: где взять, пример использования

SQLite транспорт на основе PDO для Symfony Messenger

SQLite транспорт сообщений на основе PDO можно взять отсюда: https://github.com/balpom/sql-messenger

На самом деле, я немного смухлевал ;-) и с нуля ничего не писал, а просто переделал Doctrine Transport, упростив всё, что только можно.

Так как для работы с SQLite используется PDO, то, скорее всего, после некоторой доработки напильником эту мою поделку можно будет использовать и со всеми остальными БД, которые поддерживает PDO.

Пример использования SQLite транспорта на основе PDO для Symfony Messenger

Пример использования SQLite транспорта сообщений можно взять отсюда: https://github.com/balpom/symfony-messenger-sqlite

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

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

Подробно расписывал в статье https://habr.com/ru/articles/817425/, здесь пробегусь по минимуму.

Запуск Worker«а (имитирует отправку SMS):
php bin/console messenger:consume sql-async
(В конце именно sql-async, а не doctrine-async.)

Добавление сообщений в очередь (открывать в отдельной консоли):
php tests/send.php

Мягкая остановка Worker«ов:
php bin/console messenger:stop-workers

Сравнительное тестирование SQLite транспорта для Symfony Messenger

Для эксперимента были сделаны две отдельные инсталляции двух примеров:
https://github.com/balpom/symfony-messenger-sample — Doctrine transport
https://github.com/balpom/symfony-messenger-sqlite — мой самодельный транспорт

Для тестирования производительности был создан простой скрипт tests/addtime.php, добавляющий в очередь заданное количество сообщений и замеряющий истраченное время (добавлен в вышеуказанные примеры).

$message = 'Simple message';
$total = 1000;
echo 'Start ' . $total . ' messages adding...' . PHP_EOL;
$start = microtime(true);
for ($i = 1; $i <= $total; $i++) {
    $sms = new SmsNotification($message);
    $bus->dispatch($sms, [new BusNameStamp($busName)]);
}
echo (microtime(true) - $start) . ' sec' . PHP_EOL;

Тестирование проводилось для БД SQLite, размещённых на жёстком диске и на RAM-диске.
Использовалось различное число сообщений. Для чистоты эксперимента перед каждым замером «старая» база удалялась.
Процессор — старый добрый Xeon X5650 (6×2,67GHz HT) LGA1366.

Результаты тестирования doctrine-messenger и sql-messenger.

Результаты тестирования doctrine-messenger и sql-messenger.

Как видно, 1000 сообщений в очередь на жёстком диске мой транспорт добавляет за ~68 секунд, а Doctrine transport — за ~152 секунды. Разница в ~2.2 раза! Вообще, думал будет процентов 15–20…

На RAM-диске — да, всё летает. :-) Те же 1000 сообщений добавляются за 0.16 и 0.31 сек соответственно. Миллион — за 146 и 269 секунд соответственно.

Что интересно — база на миллион сообщений, созданная Doctrine, почему-то на ~0.3% больше базы, созданной моей поделкой.

Размеры баз, созданных sql-messenger и doctrine-messenger.

Размеры баз, созданных sql-messenger и doctrine-messenger.

Послесловие

Ну не знаю… Было интересно повнимательнее посмотреть и почувствовать, как оно там внутри Symfony Messenger устроено и данный опыт, безусловно, очень полезен.
Матёрые PHP профи почти наверняка найдут кучу изъянов в моём коде — ну и ладно…

Догадывался, что SQLite на жёстком диске будет тормозить, но не думал, что настолько: 1000 сообщений за 68 секунд — это ~15 сообщений в секунду.
Про Doctrine с её ~6.5 сообщений в секунду я вообще молчу…
Интересно, а MySQL как проявляет себя в очереди?

Да, получается, что на RAM-диске SQLite быстрее в ~400–500 раз (ну да, RAM-диск всё-таки :-)).

В-общем, очевидно, что практическое применение SQLite в качестве хранилища очередей крайне ограничено какими-то простыми или учебными задачами.

Хотя… ;-)

© Habrahabr.ru