Давным-давно я публиковал статью на хабре, как написать свой вебсокет-сервер с нуля. Статья переросла в библиотеку. Несколько месяцев я занимался её развитием, ещё несколько лет — поддержкой и багфиксом. Написал модуль интеграции с yii2. Какой-то энтузиаст написал интеграцию с laravel. Моя библиотека совместима с php7. Недавно я решил отказаться от её дальнейшей поддержки (причины ниже), поэтому хочу помочь её пользователям перейти на другую библиотеку.



Прежде чем начать писать свой вебсокет-сервер, я выбирал из готовых продуктов, и на тот момент их было всего два: phpdaemon и ratchet.

phpdaemon


1400 звёзд на гитхабе

  • зависит от установки библиотеки libevent
  • протоколы: HTTP, FastCGI, FlashPolicy, Ident, Socks4/5.

Ratchet


3600 звёзд на гитхабе

  • тянет за собой около десятка зависимостей
  • протоколы: websocket, http, wamp
  • поддержка windows
  • нет ssl

Эти библиотеки были очень монструозны и при этом не соответствовали моим внутренним требованиям:

  • отсутствие зависимостей
  • наличие таймеров

Таймеры мне нужны были для написания игры на вебсокетах для расчёта взаимодействий между всеми пользователями каждые 0.05 секунды.

В итоге я написал библиотеку для себя и поделился ею с сообществом на гитхабе. Сделал несколько демок (в том числе игру «танчики»). Переписал стороннюю игру (с разрешения авторов) с node.js на свою библиотеку. Делал нагрузочное тестирование. Демки работали годами без перезагрузки. Старался отвечать на тикеты в течения дня. Всё это показывало, что моя библиотека может быть использована на продакшене и многие её использовали.

Была единственная проблема. Мне хватало моей библиотеки для использования в своих проектах, а вот другим нет. Они хотели, чтобы я её развивал, а мне это было не нужно. Кому-то требовалась поддержка windows, а кому-то ssl, pg_notify, safari, pthreads и многое другое. Открытые тикеты с запросами на реализацию различного функционала висят годами.

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

Workerman


4500 звёзд на гитхабе

  • отсутствие зависимостей
  • протоколы: websocket, http/https, tcp, сustom
  • поддержка таймеров
  • интеграция с react-компонентами
  • поддержка windows

Первый его релиз был ещё два года назад, но почему-то всё новые и новые люди начинали пользоваться моей библиотекой для новых проектов. Я ещё могу понять, что ею пользуются на старых проектах (работает — не трогай), но на новых… — для меня это была загадка.

Если загуглить «php websocket», то первая страница — это моя статья на Хабре, а вторая — «Ratchet», который кому-то может показаться сложным и он выберет из-за этого мою библиотеку или вообще откажется от идеи делать вебсокеты.

Что ж, пришло время исправить эту досадную ошибку и донести до как можно большего количества людей о существовании такой библиотеки как Workerman и привести несколько примеров по её использованию.

На главной странице проекта в гитхабе уже есть несколько примеров. Рассмотрим один из них:

websocket server
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;

// Create a Websocket server
$ws_worker = new Worker("websocket://0.0.0.0:8000");

// 4 processes
$ws_worker->count = 4;

// Emitted when new connection come
$ws_worker->onConnect = function($connection)
{
    echo "New connection\n";
 };

// Emitted when data received
$ws_worker->onMessage = function($connection, $data)
{
    // Send hello $data
    $connection->send('hello ' . $data);
};

// Emitted when connection closed
$ws_worker->onClose = function($connection)
{
    echo "Connection closed\n";
};

// Run worker
Worker::runAll();
tcp server
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;

// #### create socket and listen 1234 port ####
$tcp_worker = new Worker("tcp://0.0.0.0:1234");

// 4 processes
$tcp_worker->count = 4;

// Emitted when new connection come
$tcp_worker->onConnect = function($connection)
{
    echo "New Connection\n";
};

// Emitted when data received
$tcp_worker->onMessage = function($connection, $data)
{
    // send data to client
    $connection->send("hello $data \n");
};

// Emitted when new connection come
$tcp_worker->onClose = function($connection)
{
    echo "Connection closed\n";
};

Worker::runAll();

Чтобы запустить пример, нужно установить workerwan: composer require workerman/workerman
Пример можно запустить с помощью команды php test.php start и в консоли мы увидим:
----------------------- WORKERMAN -----------------------------
Workerman version:3.3.6 PHP version:7.0.15-0ubuntu0.16.10.4
------------------------ WORKERS -------------------------------
user worker listen processes status
morozovsk none websocket://0.0.0.0:8000 1 [OK]
----------------------------------------------------------------
Все команды workerman:
php test.php start
php test.php start -d -демонизировать скрипт
php test.php status
php test.php stop
php test.php restart
php test.php restart -d
php test.php reload

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

Например:

  • пользователь #1 лайкает фотографию пользователя #2 и мы хотим отправить пользователю #2 об этом уведомление, если он сейчас на сайте.
  • на сайте появилось новое объявление и мы хотим отправить уведомление нашему модератору,
    чтобы он его проверил

Из двух примеров выше можно собрать один, который будет делать то что нам нужно:

Отправка сообщения одному пользователю:
код сервера server.php:

<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;

// массив для связи соединения пользователя и необходимого нам параметра
$users = [];

// создаём ws-сервер, к которому будут подключаться все наши пользователи
$ws_worker = new Worker("websocket://0.0.0.0:8000");
// создаём обработчик, который будет выполняться при запуске ws-сервера
$ws_worker->onWorkerStart = function() use (&$users)
{
    // создаём локальный tcp-сервер, чтобы отправлять на него сообщения из кода нашего сайта
    $inner_tcp_worker = new Worker("tcp://127.0.0.1:1234");
    // создаём обработчик сообщений, который будет срабатывать,
    // когда на локальный tcp-сокет приходит сообщение
    $inner_tcp_worker->onMessage = function($connection, $data) use (&$users) {
        $data = json_decode($data);
        // отправляем сообщение пользователю по userId
        if (isset($users[$data->user])) {
            $webconnection = $users[$data->user];
            $webconnection->send($data->message);
        }
    };
    $inner_tcp_worker->listen();
};

$ws_worker->onConnect = function($connection) use (&$users)
{
    $connection->onWebSocketConnect = function($connection) use (&$users)
    {
        // при подключении нового пользователя сохраняем get-параметр, который же сами и передали со страницы сайта
        $users[$_GET['user']] = $connection;
        // вместо get-параметра можно также использовать параметр из cookie, например $_COOKIE['PHPSESSID']
    };
};

$ws_worker->onClose = function($connection) use(&$users)
{
    // удаляем параметр при отключении пользователя
    $user = array_search($connection, $users);
    unset($users[$user]);
};

// Run worker
Worker::runAll();

код клиента client.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <script>
        ws = new WebSocket("ws://127.0.0.1:8000/?user=tester01");
        ws.onmessage = function(evt) {alert(evt.data);};
    </script>
</head>
</html>

код отправки сообщений с нашего сайта send.php:

<?php
$localsocket = 'tcp://127.0.0.1:1234';
$user = 'tester01';
$message = 'test';

// соединяемся с локальным tcp-сервером
$instance = stream_socket_client($localsocket);
// отправляем сообщение
fwrite($instance, json_encode(['user' => $user, 'message' => $message])  . "\n");

Справедливости ради я решил написать такой же пример для ratchet, но документация мне не помогла, как 3 года назад. Зато на stackoverflow предложили немного костыльный, но рабочий вариант: соединяться из своего php-скрипта по ws-соединению. Конечно это не так же просто как соединиться с tcp-сокетом с помощью stream_socket_client и отправить сообщение с помощью fwrite. Но уже что-то.

Плюс ещё остался для меня незакрытый вопрос: поддерживает ли ratchet возможность запуска нескольких воркеров и если да, то как в таком случае отправлять сообщение одному пользователю, ведь не понятно на каком он воркере. На workerman это можно сделать так.

В общем, я выбрал для себя библиотеку Workerman и рекомендую переходить на неё пользователям моей библиотеки. Все примеры лежат на гитхабе.

Update: в комментариях рекомендуют swoole. Я натыкался на эту библиотеку ранее, но у меня сложилось ложное впечатление, что что она не поддерживает php7 и после этого она выпала из моего круга зрения. А зря. Интересная библиотека.
Что вы используете для вебсокетов на сервере?

Проголосовало 239 человек. Воздержалось 105 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться с друзьями
-->

Комментарии (53)


  1. SerafimArts
    10.07.2017 05:29

    Ещё Swoole потеряли


  1. DeLuxis
    10.07.2017 06:59
    +1

    Забавно видеть Ratchet, Workerman и Swoole в вышестоящем комментарии выделенных цветом уже когда-то посещенных ссылок. На Workerman вовсе стоит звездочка.

    Однако я использую ваше решение так как изначально начал реализовывать на нем. Пришлось в некоторых местах костылить, в частности из-за необходимости запуска рассылки в отдельном процессе. Делал тогда на 5.6, поэтому использовал форк, предварительно закрывая соединение к базе.

    Сейчас перейдя на 7.0 очень хочу переписать на pthreads. И все это не вмешиваясь в исходники вашей библиотеки. Да костыльно, мне это не нравится, но времени нет.

    В планах переписать websocket сервер на Golang. Преимуществ там уйма и PHP просто не предназначен для решения таких типов задач.


    1. SerafimArts
      10.07.2017 09:45

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


      1. DeLuxis
        10.07.2017 09:53
        +1

        1. Компилируемый, не надо тянуть интерпретатор и другие сопутствующие минусы.
        2. Готовые библиотеки для сокетов, в том числе веб.
        3. С легкостью держит большое количество клиентов.
        4. pthreads в php все же является сторонним решением.

        И мне просто он нравится, хочу задействовать по полной программе.


        1. SerafimArts
          10.07.2017 11:48
          +1

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

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


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


          1. speller
            11.07.2017 03:02

            «На порядок» = «в 10 раз». «Несколько порядков» = «в 100 раз», «в 1000 раз» и т.д. На будущее )


        1. krocos
          10.07.2017 13:58

          Лично меня смущает в переходе на го, не то, что птреды сторонние, ибо написаны Ваткинсом, который дофига в пхп вложил, не тем, что php резкий как незнаючто с 7ой версии, а то, что я не хочу становиться автором какой-нить навороченой либы для, например, каких-нить офисных документов… Ну, короче говоря, боязнь недостатка пакетов.


    1. krocos
      10.07.2017 11:31

      Тоже были порывы использовать pthreads. Но показалось хлопотным собирать php с zts постоянно. Но потом нам стало очевидным, что лучший способ параллелить задачи на php – очерди. Тем более есть такие прекрасные решения как beanstalk и rabbitmq.


  1. Gemorroj
    10.07.2017 07:14
    -1

    ssl в ratchet завезли, но в еще не выпущеной ветке 0.4 (https://github.com/ratchetphp/Ratchet/commit/369227dc1c0e5e016e729d8ab4789a0fd0ba85e4)


    1. krocos
      10.07.2017 11:32

      А ProxyPass через апач вроде хорошо работает, и более простое решение имхо, особенно учитывая certbot


  1. MetaDone
    10.07.2017 08:51
    +1

    существует еще https://github.com/amphp/socket


    1. krocos
      10.07.2017 11:56

      Ну это типа react'а, но поверх надо же еще веб-сокеты нагородить. Проще воспольльзоваться либой где уже есть этот функционал? Хотя как сокет это имхо очень прилично, но вот именно я не разобрался нормально, мне показалось, что сложней чем реакт. Я еще подумал, что проще тогда swoole.


      1. MetaDone
        10.07.2017 12:05

        виноват, слегка попутал со ссылкой
        вот правильная — https://github.com/amphp/websocket


        1. krocos
          10.07.2017 12:37

          О, а я чет не стал искать, но уже всё есть, круто. Я сильно не вдавался в детали, посмотрел несколько примеров и мне кажется, что на колбэках как-то попроще будет, чем на елде выезжать :)


          1. SerafimArts
            10.07.2017 17:41
            +1

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


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


            1. MetaDone
              10.07.2017 18:07

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

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


              1. SerafimArts
                10.07.2017 20:41

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


                1. mayorovp
                  10.07.2017 21:04
                  +1

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


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


                  1. SerafimArts
                    10.07.2017 21:45

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


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


  1. krocos
    10.07.2017 11:28
    +2

    Да, есть еще 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, ибо подходит хорошо по производительности и степени удобства при конструировании сервера.


    1. mayorovp
      10.07.2017 11:33
      -1

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


      1. krocos
        10.07.2017 11:46

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


        1. mayorovp
          10.07.2017 13:44

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


          1. krocos
            10.07.2017 14:03

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


            1. mayorovp
              10.07.2017 14:11

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


              1. krocos
                10.07.2017 14:29

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


                1. mayorovp
                  10.07.2017 14:39

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


                  1. krocos
                    10.07.2017 15:44

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


                    1. mayorovp
                      10.07.2017 15:55

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


                      1. krocos
                        10.07.2017 15:57

                        Читайте ниже/дальше, там тоже самое да потому же, но другими словами


        1. BoShurik
          10.07.2017 14:22

          1. krocos
            10.07.2017 15:09

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


            1. BoShurik
              10.07.2017 15:45
              +1

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


              • Дополнительная теория (реализация очень близка к моей)
              • Надо знать как пользоваться React\ChildProcess\Process


              1. krocos
                10.07.2017 15:54

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

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


                1. mayorovp
                  10.07.2017 15:58

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


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


                  1. krocos
                    10.07.2017 16:04

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


                    1. mayorovp
                      10.07.2017 16:13

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


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


                      1. krocos
                        10.07.2017 16:20

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

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


                        1. mayorovp
                          10.07.2017 16:34

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


                    1. krocos
                      10.07.2017 16:15

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


                1. BoShurik
                  10.07.2017 16:15

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

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


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


                  1. krocos
                    10.07.2017 16:30

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

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

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


                    1. BoShurik
                      10.07.2017 16:47
                      +1

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


                      1. krocos
                        10.07.2017 17:21
                        -1

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

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

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

                        Либо мы не понимаем друг-друга, либо вы – знатные тролли, господа :)


  1. porn
    10.07.2017 11:29
    +1

    Ещё есть voryx/thruway. Поддерживает WAMPv2. Бандл для симфони.


    1. samizdam
      11.07.2017 19:56

      Ну, это вообще-то либа с WAMP-роутером и клиентами, а не чисто веб-сокет.
      Для вебсокета, она как раз использует Ratchet (как один из возможных транспортов).


      1. porn
        11.07.2017 21:58

        Да, так и есть. Это по сути готовое решение, причём очень неплохое. Нам ведь бизнес-задачи нужно решать?


  1. 408dev
    10.07.2017 17:36
    +1

    У автора отличная библиотека для вебсокетов, но использовав ее для интенсивных нагрузок, периодически (может быть раз в день, без видимой зависимости от внешних факторов) возникал block на пару минут на записи в поток (синхронные сокеты). Хотя все теоретические тонкие моменты были предусмотрены (проверка готовности, проверки полных отправленных данных и тп). На Ratchet-е все ок.
    Но коментарий не камень в огород, а благодарность за работу — либка хорошо написана и было удобно использовать!


  1. igordata
    11.07.2017 19:25
    +2

    После знакомства с https://github.com/centrifugal/centrifugo я для себя этот вопрос закрыл раз и навсегда
    Спасибо fzambia


    1. Fesor
      12.07.2017 01:21

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


      1. samizdam
        12.07.2017 11:43

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

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


        1. Fesor
          12.07.2017 21:10

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


  1. korvinko
    11.07.2017 22:15
    +2

    Когда начинали проект решили попробовать centrifugo, как оказалось этот проект идеально нам подходит. По набору возможностей и эффективности впереди Ratchet и вообще любого web socket сервера что я пробовал. Автор оперативно отвечает на issue и принимает feature request. Спасибо fzambia за плодотворное сотрудничество.