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

Комментарии 58

НЛО прилетело и опубликовало эту надпись здесь

Преимущество у Go перед PHP ровно одно — это скорость. А какие ещё, раз уж вы говорите об "уйме"? По-мне, так наоборот.

НЛО прилетело и опубликовало эту надпись здесь
  1. Это преимущество высосано из пальца, будем чествными.
  2. Это не преимущество, под пых их на несколько порядков больше.
  3. Зависит от реализации, конечно же, но в целом да, вы повторили мой пункт о скорости.
  4. Тот же самый пункт о скорости.

В целом, кроме повтора тезиса "он быстрее" я не услышал никаких реальных оправданий его использования. Ну разве, только "он мне нравится", лично для меня это был самый убедительный аргумент. Т.к. язык, на котором нравится писать будет на порядок профитнее в умелых руках. Но это уже сугубо личное для каждого.


Если постараться оценивать объективно, то пых и мощнее как язык, и экосистема в "веб-сфере" у него на порядок взрослее, и при базе на том же swoole, есть подозрения, что будет побыстрее большинства реализаций на Go, но это вообще не точно =)

«На порядок» = «в 10 раз». «Несколько порядков» = «в 100 раз», «в 1000 раз» и т.д. На будущее )
Лично меня смущает в переходе на го, не то, что птреды сторонние, ибо написаны Ваткинсом, который дофига в пхп вложил, не тем, что php резкий как незнаючто с 7ой версии, а то, что я не хочу становиться автором какой-нить навороченой либы для, например, каких-нить офисных документов… Ну, короче говоря, боязнь недостатка пакетов.
Тоже были порывы использовать pthreads. Но показалось хлопотным собирать php с zts постоянно. Но потом нам стало очевидным, что лучший способ параллелить задачи на php – очерди. Тем более есть такие прекрасные решения как beanstalk и rabbitmq.
А ProxyPass через апач вроде хорошо работает, и более простое решение имхо, особенно учитывая certbot
Ну это типа react'а, но поверх надо же еще веб-сокеты нагородить. Проще воспольльзоваться либой где уже есть этот функционал? Хотя как сокет это имхо очень прилично, но вот именно я не разобрался нормально, мне показалось, что сложней чем реакт. Я еще подумал, что проще тогда swoole.
виноват, слегка попутал со ссылкой
вот правильная — https://github.com/amphp/websocket
О, а я чет не стал искать, но уже всё есть, круто. Я сильно не вдавался в детали, посмотрел несколько примеров и мне кажется, что на колбэках как-то попроще будет, чем на елде выезжать :)

Да Amp вообще довольно дикая штука и написана большей половиной в хипстерском функциональном стиле. Лично у меня, как и любого php-шника, пережившего "эпоху DLE" (ну т.е. докомпозеровские времена) развилась фобия подобных штук. Не, ну хоть бы в класс закатали, да статики наружу прокинули бы...


Так что учитывая всё это, я отдаю свой голос за React-based штукенции и даже не по причине йилдов, а просто код кажется более опрятным и читаемым, нежели пачка хелпер-функций ядра.

Да Amp вообще довольно дикая штука и написана большей половиной в хипстерском функциональном стиле

а можно пример? бегло пробежался по репозиториям — вроде все норм

Прям самое сердце этой либы: https://github.com/amphp/amp/blob/master/lib/functions.php Сейчас уже поопрятнее стало, с год назад вообще жуть творилась в этом файлике. Тысяча строк функций и процедурок.

Сейчас — обычная либа для асинхронности. Ни вижу ничего особо страшного, всего-то сделали аналог async/await через генераторы. Обычное дело для языков, где еще нет async/await, но уже есть генераторы. Через это прошли Python, C# и Javascript. Теперь вот очередь PHP.


Вот год назад, согласен — был кошмар. Только не надо называть таку свалку из функций "функциональным стилем" — в ФП любят чистые функции, а там первая же функция — с сайд-эффектом.

"функциональный стиль" я для красного словца упомянул с некоторой толикой иронии над JS. Нынче просто они любят подобную шляпу разводить и считают это нормой. Правда, в защиту можно сказать, что почти всегда она не уезжает за пределы модуля, но не суть. Надеюсь перерастут, как PHP в своё время, исходники какого-нибудь Ангулара почти что вменяемые, даже. Далеко не уровня Symfony или Laravel, но некоторый прогресс есть.


Ну и смотрели\использовали эту либу в продакшене как раз год назад примерно, вот и запомнился этот файлик и любовь мейнтейнеров к километровй кучке функций. Потом забили болт и переехали на Газзл, т.к. на тот момент она (библиотека Amp, а точнее сборка Artax) все логи спамила ошибками (которые гасятся через собаку, что не отменяет наличие самих сообщений об ошибках). Сейчас, к слову, они тоже остались, но не в таком количестве, конечно.

Да, есть еще Swoole, но вот мы не используем его ибо у него с таск-воркерами (как нам показалось) странно сделано (жеско задается кол-во воркеров только при старте и, если передать таск несуществующему воркеру, то будет капец). Ну, а в целом Swoole – первый кандидат. Про него даже писали (точно не помню где), что из php сделали nodejs.
А вообще мы используем Ratchet. А схема работы в тяжелых случаях такая. Сам WS-сервер работает как шина, тоесть только передает сообщения. Ну и вот когда, например, приходит rpc-вызов на тяжелый таск, то это сообщение отправляется в очередь. Собственно это – элемент масштабирования. Можно поднять сколько надо воркеров, да еще и на разных серверах, если не будет хватать «сработки» при текущем количестве консумеров.
Для нас было самым большим открытием – это как вернуть ответ. Сначала консумер сообщался с ws-сервером по ws же протоколу. Но так мы имеем мега оверхэд по вермени затрачиваемому на само соединение. Особенно это заметно когда передается очень много сообщений на ws. Надо было как-то упростить схему. После продолжительных поисков был найден React Child Process Component. Он решил все проблемы с производительностью. И ответ теперь возвращается так: В loop react'а добавляется дочерний процесс, который поднимает ratchet же tcp-сервер. Тоесть консумер передает ответ через небольшую обертку над fsockopen на tcp сервер, который является процессом петли ws сервера и ему (дочернему процессу) остается только выдать сообщение в stdout и он попадет в onData обработчик дочернего процесса. А тут уже можно выбрать соединение и отправить ответ.
Тут остается только не забывать передавать sigterm на дочерние процессы-серверы-слушатели ответов, когда гасите сервер. Да, тут еще подоспела новая возможность PHP – pcntl_async_signals(). Специально для ratchet'а наверно писали, для диспатчинга в нем сигналов :)
Собственно поэтому и используем Ratchet, ибо подходит хорошо по производительности и степени удобства при конструировании сервера.

А через очередь ответ не передать никак?

Через очередь в ту же самую петлю событий, ответить в тот же сокет, который прислал запрос? То есть, тут вопрос в том, как в петлю событий добавить прослушку сокетов ws и amqp?

Ну, должен же тут быть какой-то способ. Вряд ли вы — первый кому захотелось обрабатывать ws и amqp в одном цикле.

Так наверняка на всех этих либах есть способ, но вот я считаю, что он, в смысле понимания, должен быть прост, а, в смысле скорости, не уступать другим решениям. Мне показалось что ратчет поверх реакта с дочерними процессами в петле событий – это самое и быстрое и простое одновременно. То есть, баланс скорости и простоты соблюден хорошо.
А еще, это дает свободу творчества :) Можно в этом дочернем процессе что угодно сделать. Хоть еще дочерних процессов напихать, каждый из которых сам сокет сервер. Кароче, как я и говорил, понятное и производительное решение.

Э… а что сложного в подходе вида "вот ws-сокет, вот amqp-сокет, вот мы их слушаем в цикле"?

Теоретически – ничего. Но это надо уметь напрограмировать так, что бы моск не вытек. Надо еще, помимо того, что добавить второй сокет на прослушку, понимать начать AMQP протокол (ну или любой другой, то есть, данные из сокета надо как-то же обрабатывать). Поэтому, нужны знания stream_socket_client и прочих функций или их оберток в той либе, которую вы используете. А тут получается, что дополнительные процессы добавляются в петлю, посредством прослушки stdout процесса. То есть, тут теория еще проще. Мы знаем, что слушаем появление данных, да еще и в красивой и удобной обертке. Я бы сравнил это с maven :) Плагины писать просто и можно делать на любом другом языке, ибо общение между плагинами и maven идет через stdout/stdin, если я не ошибаюсь. Вы понимаете преимущество возможности запустить процесс и слушать его stdput? Это же не обязательно может быть php, а, например, тот же го.

В том же цикле что и сокеты? Вы понимаете, что stdout процесса сильнее отличается от сокета чем amqp от ws?

Да я-то про это и говорю, что не напрямую подключать заморочки, а подключать процессы, которые сами по себе могут быть чем угодно: сокетным сервером, который консумит amqp или квэрит данные из бд, или еще чем угодно и на чем угодно написаным, хоть на джаве, хоть на го… Ну вы поняли. Главное, что бы сам вебсокет оставался тупой шиной с легким подключением этих процессов. Это, заметте, быстрая и простая передача данных. Вот пример (я его еще в другом ответе ниже оставил): gist

Перечитал ваш комментарий три раза, но так и не понял.

Читайте ниже/дальше, там тоже самое да потому же, но другими словами
Бесспорно, вы красиво всё добавили в петлю, но:
— Это дополнительные либы
— Дополнительная теория
А что, если (тфутфуконечно) начнется усложение, увеличение нагрузки?
Надо знать как пользоваться \React\Promise\all, \Bunny\Async\Client, React\ZMQ\Context…
Не проще ли как-то так?
Как мне кажется, это более универсально. Я имею в виду добавление получения данных откуда-то в сокет.

Надо сказать, что минусы применимы и к вашему методу :)


  • Дополнительная теория (реализация очень близка к моей)
  • Надо знать как пользоваться React\ChildProcess\Process
С одной стороны вы правы, что надо знать «модуль» реакта, но в моем примере минус исчислимый – один, а в вашем примере, количество минусов может варьироваться в зависимости от того, что надо подключать к петле событий, тоесть:
— Теория/абстракции несравнимо проще (либо промисы/контексты/корутины/елды/ватева, либо in/out/err выхлоп процесса)
— Универсальность: можно подключить программу на любом языке, которая являться может чем угодно

Мне кажется или мы про Яблоки vs. Апельсины? :)

Видите ли — дочерний процесс, он не из воздуха берется. Его тоже еще надо написать.


Если вам дочерний процесс достался от другого разработчика — то, конечно же, проще запустить его как вы показали. Но если стоит задача переслать запрос от ws-клиента в amqp-очередь, а потом вернуть клиенту ответ — то проще обойтись без дочернего процесса: будет на 1 требующий изучения слой меньше.

На один ли меньше? Надо «подключить» к петле промис/контекст/корутину/елду или как там называется абстракция которую запрограммировали разработчики, что бы подключить реакцию на данные из сокета… В общем не меньше. Подключать придется. Но можно изучать как подключать каждое что там надо подключить или 1 метод. Поэтому я и считаю, что это проще/меньше теории и универсальней.

Нет, вам надо один раз разобраться как передавать асинхронные события в вашу петлю, после чего использовать этот способ где угодно. Базовых абстракций всего две — колбеки и корутины, обе сводятся к месту, где вы можете написать какой пожелаете код.


PS надоело читать ругательства вместо аргументов. Ловите минус в карму за "елду".

Это в теории абстракций две. Но разработчики либ могут их добвить/назвать по другому – больше теории.

И какая разница, звОним мы или звонИм? Дозваниваемся же…

Ну и зачем вам такие либы? Найдите другую, более понятную.

В общем, суть не в том, что делает сам процесс, а в методе подключения реакции на данные в сервер. Мы изначально не разделили понятия. Я говорю про то, как подключить данные, а не как получать, когда они придут.
Мне кажется или мы про Яблоки vs. Апельсины? :)

Весьма вероятно :)


Ваша универсальность тоже имеет свои границы.
Во-первых, не понятно как быть если процесс отвалился с ошибкой? Во-вторых, придется как-то парсить stdout, т.к. он все-таки предназначен для чтения пользователем, а не машиной.

Вы же знаете, что подключаете? Скрипт, который вы написали или сторонняя программа через обертку? В любом случае, не стоит конечно подключать что-то абсолютно не имея представления о том что там идет в вывод…

На счет ошибок: еще есть stderr.

На счет выпадения скрипта полностью. Есть возможность сделать так:
$process->on('exit', function($exitCode, $termSignal) use ($process) {
$process->start($server->loop);
// или через фабрику
});
А в целом конечно лучше через обертку запускать (хотя это тоже под большим вопросом), которая сама будет там ответственна за перезапуск отвалившихся частей.
Полагаю, лучше всё это на практике потестить.

И тут мы пришли к тому, что вам писал mayorovp. На один слой больше за счет такой вот обертки.
Либо имеем нарушение принципа единственной ответственности, когда вебсокет сервер у нас еще занимается парсингом вывода и перезапуском упавших процессов.

«нарушение принципа единственной ответственности»
Вебсокет сервер при методе, который я описываю, продолжает выполнять 1 роль – быть тупой шиной и перегонять данные от бэкенда к клиентам.

«парсингом вывода»
Это делают процессы, которые он запускает

«и перезапуском упавших процессов»
По этой де логике можно сказать, что, да, мы нарушаем принцип единой ответственности ибо сервер реагирует не только на ondata, но и на onopen, onclose, onerror, вызывает методы контроллеров при событиях…

Либо мы не понимаем друг-друга, либо вы – знатные тролли, господа :)
Ну, это вообще-то либа с WAMP-роутером и клиентами, а не чисто веб-сокет.
Для вебсокета, она как раз использует Ratchet (как один из возможных транспортов).
Да, так и есть. Это по сути готовое решение, причём очень неплохое. Нам ведь бизнес-задачи нужно решать?
У автора отличная библиотека для вебсокетов, но использовав ее для интенсивных нагрузок, периодически (может быть раз в день, без видимой зависимости от внешних факторов) возникал block на пару минут на записи в поток (синхронные сокеты). Хотя все теоретические тонкие моменты были предусмотрены (проверка готовности, проверки полных отправленных данных и тп). На Ratchet-е все ок.
Но коментарий не камень в огород, а благодарность за работу — либка хорошо написана и было удобно использовать!

Аналогично, уже 2 проекта с ним делаем, до этого писали свои поделки на socket-io. Был один проект на Ratchet года 2 назад и как-то что-то не рекомендую больше никому так делать.

Был один проект на Ratchet года 2 назад и как-то что-то не рекомендую больше никому так делать.

А не поделитесь какие именно проблемы? В разработке и (или) эксплуатации?

В разработке он не так удобен как связка из php + nodejs + socket-io через zeromq какой. На тот момент были какие-то проблемы с реализацией протокола, всех нюансов уже не упомню. Ну и отдельная проблема — найти библиотеки с учетом event loop и т.д. практически невозможно, потому по итогу вышло всеравно так что от основного приложения мы отделили это все через zeromq. А если так — то на кой черт websockts на php непонятно.

Когда начинали проект решили попробовать centrifugo, как оказалось этот проект идеально нам подходит. По набору возможностей и эффективности впереди Ratchet и вообще любого web socket сервера что я пробовал. Автор оперативно отвечает на issue и принимает feature request. Спасибо fzambia за плодотворное сотрудничество.
Я в своё время столкнувшесь с задачей реализации чата пошёл несколько дальше в области велосипедо строения чем написание своей библитоки на php. Так как я на тот момент знал не только php и JavaScript но ещё и C++ то я решил проблему работы с вебсокетами путём написания отдельного многопоточного сервера на C++.
В итоге получился проект CppComet
Вообще-то, Ratchet основан на ReactPHP, а тот прекрасно поддерживает таймеры.
Для возможности использовать таймеры необходим доступ к EventLoop. Насколько я понял взглянув на код, EventLoop нужно создать самому и передать в конструктор Ratchet\App, иначе доступа к нему не будет. Пример:

$loop = \React\EventLoop\Factory::create();
$app = new Ratchet\App('localhost', 8080, '127.0.0.1', $loop);
// ...

$loop->addPeriodicTimer(1, function() {
    echo "Timer fired!" . PHP_EOL;
});

$app->run();
Можно рассмотреть как альтернативу легковесной либы server+client, с разбором uri и возможностью создания дочерних процессов
github.com/arthurkushman/php-wss
ArthurKushman
как альтернативу легковесной либы
Создание процессов — не очень легковесно, а stream_select — тоже не очень оптимальный вариант по ресурсам, к тому же ограничен одной тысячью процессов.
Лучше использовать библиотеки libevent/event. Например, workerman использует и не подвержен описанным выше проблемам и ограничениям. Также есть swoole, который тоже без таких проблем.
Именно благодаря существованию workerman и swoole я отказался от развития и поддержки своего велосипеда github.com/morozovsk/websocket
Существующие решения меня более чем устраивают, имеют большое комьюнити и т.д. Отказываться от них я планирую, только когда поддержка вебсокетов будет из коробки, ориентировочно с выходом PHP 8.
Ссылку на вашу библиотеку + описание можете отправить автору php-дайжеста, с просьбой включить её в следующий дайджест. Дайджест читает очень много людей, это поможет найти единомышленников, которые захотят её использовать / поддерживать.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации