Комментарии 30
Не очень знаком с PHP. Но мне кажется можно еще создать кольцевой буффер сообщений и форкнуть процесс отправки почты. Получится что-то вроде внутренней очереди.
Еще есть возможность отдать контент пользователю(fastcgi_finish_request())и продолжить выполнять работу.
Да, если нагрузки не очень большие. В противном случае повышается риск того, что все fpm воркеры будут заняты и не смогут обрабатывать новые запросы.
Еще можно под это дело сделать отдельный fpm pool, что бы не блокировать основной. Так то этой мысли сто лет в обед https://tideways.com/profiler/blog/using-php-fpm-as-a-simple-built-in-async-queue
Кроме очередей все остальное выглядит как "смотри что я могу". Увидел бы такое в продакшене - повесил(ся) бы.
Фишка в очередях ещё в том, что у вас отправитель и получатель сообщения могут быть написаны на разных языках. Например, навороченная бизнес-логика на PHP, а отправитель почты на go. Кроме того, для "разбора" очереди можно запустить кучу обработчиков параллельно.
Ну пока что все "за" все еще говорят в пользу очередей. На мой взгляд самый главный и очевидный плюс (помимо того, что воркеры всякие могут быть реализованы на разных языках) - это контролируемая масштабируемость нагрзуки. Юзая exec и прочие подобные вещи, вы потеряете как минимум в контроле ресурсов.
Все эти exec и форки это еще тот хеппи-дебагинг будет, особенно под нагрузкой или по следам инцидента недельной давности. Если вы (или клиент) ленивый/нищий/жадный то просто используйте базу данных (или даже json файлы) + крон воркер(ы). Это потом всегда можно запихнуть в queue если что.
$future = async(static function () use ($email) { ...
$f1=function () {};
$f2=static function () {};
А в чем отличие между $f1 и $f2?
Первый работает с динамической областью видимости, а второй со статической.
Второй вариант меньше оперативки съест, но разница небольшая: бенчмарк в цикле по 1 ляму на каждый выдал, что для статики было выделено 376 метров памяти, а для динамики - 370.
------- --------------------------------- ---------------------------------
# dynanyc static
------- --------------------------------- ---------------------------------
min 0.001 ms - 0 bytes 0.001 ms - 0 bytes
max 0.0461 ms - 0 bytes 0.0324 ms - 0 bytes
avg 0.0012 ms - 0 bytes 0.0012 ms - 0 bytes
total 5864.2512 ms - 376.00 MB 5927.6448 ms - 370.00 MB
------- --------------------------------- ---------------------------------
Order - 1 - - 2 -
------- --------------------------------- ---------------------------------
$f1 = fn () => 1;
$f2 = static fn () => 1;
Benchmark::start()
->withoutData()
->iterations(1000000)
->compare([
'dyn' => fn () => $f1(),
'sta' => fn () => $f2(),
]);
Хм, это измерение породило еще больше вопросов.
А если dyn и sta поменять местами?
->compare([
'dyn' => fn () => 1,
'sta' => static fn () => 1,
]);
->compare([
'sta' => static fn () => 1,
'dyn' => fn () => 1,
]);
Разницы нет. Бенчмарк проверяет каждый процесс в отдельности, включая память, затем суммирует для получения конечной информации.
https://github.com/TheDragonCode/benchmark/blob/main/src/Services/Runner.php#L25-L36
Можно даже упростить:
Benchmark::start()
->withoutData()
->iterations(1000000)
->compare([
'dyn' => fn () => 1,
'sta' => static fn () => 1,
]);
------- ---------------------------------- --------------------------------
# dyn sta
------- ---------------------------------- --------------------------------
min 0.0006 ms - 0 bytes 0.0005 ms - 0 bytes
max 2.7244 ms - 0 bytes 3.5528 ms - 0 bytes
avg 0.00099608599999764 ms - 0 bytes 0.000936866999999 ms - 0 bytes
total 6341.1074 ms - 376.00 MB 5566.6392 ms - 370.00 MB
------- ---------------------------------- --------------------------------
Order - 2 - - 1 -
------- ---------------------------------- --------------------------------
------- --------------------------------- ----------------------------------
# sta dyn
------- --------------------------------- ----------------------------------
min 0.0005 ms - 0 bytes 0.0006 ms - 0 bytes
max 0.1225 ms - 0 bytes 0.1353 ms - 0 bytes
avg 0.0008681610000013 ms - 0 bytes 0.00089107100000028 ms - 0 bytes
total 5376.6722 ms - 376.00 MB 6132.9579 ms - 370.00 MB
------- --------------------------------- ----------------------------------
Order - 1 - - 2 -
------- --------------------------------- ----------------------------------
Хотя, память как-то странно замеряется..
Так или иначе, хоть где-то и читал что это экономит память, на деле получаем экономию на спичках.
Кстати наикривейший benchmark если много итераций не используйте версию 2.5. (В 2.0 он не показывает это на публику)
Размер 376Mb — это сколько сам бенчмар сожрал для вычисления средних значений. Это величина пропорциональна кол-ву итераций. А вот по времени отличия в рамках погрешности.
ps: пришлось собрать php8 что бы это dragon-code/benchmark запустить.
для обоих вариантов avg ~0.12us
А по итерациям:
10000000 - 3.76 GB
1000000 - 374.00 MB
100000 - 38.00 MB
10000 - 4.00 MB
А Вы думали в 23-м году код на php 5.6 работать должен?)
Состояние памяти запоминается перед стартом колбэка и сравнивается с полученным в конце. Единственное что в этом случае не учитывается, так это объём свойства класса, в которую сохраняются результаты. В версии 2.0 конечно память не показывал - в ней не было этой функциональной составляющей - отображение используемой памяти завезли в 2.1.
Вы можете абсолютно любой бенчмарк сделать, хоть свой скрипт написать. Логика его работы банально проста и умещается в три строчки. Всё остальное - сахар:
$startAt = hrtime(true);
$callback();
$timeDiff = (hrtime(true) - $startAt) / 1e+6;
Если, по Вашему мнению, это наикривейший бенч, то где ссылка на наилучший? Только не говорите что он под капотом тоже будет использовать рекомендуемую PHP функцию hrtime и чтение состояния используемой памяти через memory_get_peak_usage или memory_get_usage.
Вот на PHP 8.2 используемую память более реально можно будет отражать т.к. завезли функцию memory_reset_peak_usage. Апгрейд будет в рамках следующего мажора, а, пока что, текущего варианта за глаза хватает.
А Вы думали в 23-м году код на php 5.6
Просто в ubuntu 20.04 по умолчанию php7.4.3. Так что пришлось несколько раз собрать php-8.2.7. ( ./configure --with-openssl --enable-bcmath --with-curl )
Anonymous functions may be declared statically. This prevents them from having the current class automatically bound to them. Objects may also not be bound to them at runtime.
Так что походу этот static ни на что не влияет. И чуть более чем бесполезен.
Жуть какая. Он официально не поддерживается.
Свежий пых ставится очень легко:
sudo apt update
sudo apt install lsb-release ca-certificates apt-transport-https software-properties-common -y
sudo add-apt-repository ppa:ondrej/php
sudo apt install php8.2
Жуть какая. Он официально не поддерживается.
И в чем же жуть?
Вроде современная ось, а репозиторий старый используется. PHP 7.4 снята с поддержки в ноябре 22-го года, а в ноябре 23-го снимется версия 8.0. То есть уже "в коробке" должна быть PHP 8.2, учитывая что в ноябре этого года обещают выпустить версию 8.3.
Вот я не понимаю зачем ставить php8.2 если он протухнет уже 2026?
Что за мода пошла на скоропортящиеся продукты.
Быстрее развиваются?
Развиваются куда? В ubuntu 14.04 были поддерживались разные кодировки, в 20.04 уже нет. В windows7 можно было панель задач разместить на разных сторонах экрана, в win11 это надо делать через реестр. Если раньше numpy работал на процессорах без avx, то теперь нет.
Значит функции стали невостребованными или устаревшими. Либо разрабы посчитали их таковыми.
разрабы посчитали их таковыми
Не уверен что это зависит от разработчиков.
Да как сказать. Сомневаюсь что за реализацию продвижения фич в том же PHP отвечают маркетологи, например...
Они реализуют своё видение продукта, также публикуют RFC с некоторыми моментами, далее собирают фидбэк от других разработчиков по всему миру и смотрят что их заинтересует, что наоборот разгневает, а до чего будет до лампочки и, на основании этого, планируют разработку в том числе.
Технологии для разработчиков разрабатывают разработчики, как бы это ни звучало. А вот в какой области этот продукт будет использоваться - это уже другой вопрос.
Но, опять же, если взять тот же Windows, то они явно хотят изменить интерфейс и улучшить продукт, и делают это. А отключая функции также реагируют на потребителей. На днях краем глаза прочитал что в "проводнике" какую-то фичу хотели вырезать, типа ненужная, но именно разработчики накинулись на них и закидали тапками, те сдались и не стали вырезать. Особо не вникал в ситуацию, гуглится не сложно, если что.
они явно хотят изменить интерфейс и улучшить продукт
Они явно коммерческая организация и явно хотят больше прибыли. Для этого надо что бы что-то менялось. А что бы не сидели на "старом" надо вилами подгонять (вносить изменения так что бы они немножко ломали обратную совместимость, что бы поддерживать старые системы становилось не рентабельным и становилось не комфортным для обычных пользователей оставаться на старой ос, дабы побудить их к обновлению и постепенному переходу на подписку, что бы конечно же упростить жизнь пользователю, делая упор на безопасность, комфорт и красивое оформление дизайн обоев).
То что одна из основных целей проекта - его капитализация, не секрет.
Согласен, это основной мотив отказа от поддержки старого. Но и сам, как опен-сурс разработчик, скажу что периодически выпускаю мажорные версии своих продуктов в зависимости от того что меняю. Смотрю на тот или иной продукт спустя время и вижу что он перестал мне нравиться по той или иной причине, и переписываю его, изменяя неймспейсы классов на более логичные, или удаляя раннее реализованную механику, которая оказалось абсолютно бесполезной, или новое добавляется, и т.д.
Так что причины для такого можно найти и обосновать. Но в случае с Microsoft - капитализация 100%.
Смотришь на это все и вспоминаешь, как еще в 2012 году был Sidekiq у RoR, который делал все то же самое, использовал Redis в качестве message bus, и был легок в отладке, в случае чего.
И в php этому всему миллионы лет (тот же gearman). Дело в том что PHP ассинхронный by design (многопоточный скорей), в отличии от тех же руби или js (который ассинхронный но не многопоточный). По этому в 99% случаев не нужна никакая ассинхронность. Обычно она нужна как раз в случаях выполнения реально долгоиграющей работы (например рассылка 100500 писем). Так же с этим в 99% случаев справляется cronjob + примитивная очередь в базе данных. И только в 0.01% случаев нужен MQ. А с MQ все как у всех, Kafka, Rabbit, Redis, DBA, и тд.
Видимо я мал, глуп и не видал больших лупЗа. Но скажите мне пожалуйста, для какой бизнес логики придется делать этот огород с асинхронщиной курильщика? Чем не устраивает обычные очереди? На 99% проектах отправка почты это Job-ы для которых выделаются 2-3 очереди.
как введение Fibers в PHP 8.1 изменит перспективы для написания асинхронных программ в будущем.
Конкретно в случае с отправкой писем вообще никак; файберы это сахар над генераторами, а значит, чтобы хоть какую-то выгоду от них получить файберы должны возвращать контроль пхп пока бездействуют. В случае с отправкой писем это должно происходить после отправки письма и до ответа от почтового сервера. Хттп запросы в пыхе синхронные, никто там контроль возвращать не будет, а значит надо писать свой обработчик, который напрямую в сокеты долбится и ждет от них ответа
Автоп путает "асинхронность" и "паралельность" - это разные вещи.
Разбираемся с асинхронным PHP