Предыстория
В процессе изучения 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 (6x2,67GHz HT) LGA1366.
Как видно, 1000 сообщений в очередь на жёстком диске мой транспорт добавляет за ~68 секунд, а Doctrine transport - за ~152 секунды. Разница в ~2.2 раза! Вообще, думал будет процентов 15-20...
На RAM-диске — да, всё летает. :-) Те же 1000 сообщений добавляются за 0.16 и 0.31 сек соответственно. Миллион - за 146 и 269 секунд соответственно.
Что интересно - база на миллион сообщений, созданная Doctrine, почему-то на ~0.3% больше базы, созданной моей поделкой.
Послесловие
Ну не знаю… Было интересно повнимательнее посмотреть и почувствовать, как оно там внутри Symfony Messenger устроено и данный опыт, безусловно, очень полезен.
Матёрые PHP профи почти наверняка найдут кучу изъянов в моём коде — ну и ладно...
Догадывался, что SQLite на жёстком диске будет тормозить, но не думал, что настолько: 1000 сообщений за 68 секунд — это ~15 сообщений в секунду.
Про Doctrine с её ~6.5 сообщений в секунду я вообще молчу...Интересно, а MySQL как проявляет себя в очереди?
Да, получается, что на RAM-диске SQLite быстрее в ~400-500 раз (ну да, RAM-диск всё-таки :-) ).
В-общем, очевидно, что практическое применение SQLite в качестве хранилища очередей крайне ограничено какими-то простыми или учебными задачами.
Хотя… ;-)