Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Делаем вебсокеты на PHP с нуля. Часть 2. IPC

Anna | 29.05.2014 | нет комментариев

После написания моей предыдущей статьи Делаем вебсокеты на PHP с нуля я осознал, что у сообщества есть определенный интерес к поднятой мной теме.

В прошлой статье я обещал, что опишу:

  • запуск нескольких процессов для обработки соединений
  • межпроцессное взаимодействие
  • распределение процессов мастер-воркер
  • проксирование вебсокетов с поддержкой nginx
  • запуск из консоли
  • интеграция с вашим фреймворком на примере yii
  • демонстрация

И, как традиционно, — получившийся код и ссылка на демонстрационный чат в конце статьи.

Запуск нескольких процессов для обработки соединений

Для работы простого сервера вебсокетов довольно одного процесса, но Дабы увеличить число одновременных соединений (и обойти лимитация 1024 одновременных соединения), а также для применения источников каждого процессора (а не только одного ядра), нужно, Дабы сервер вебсокетов применял несколько процессов (оптимально — число процессов = число ядер процессора).

Для запуска нескольких процессов мы будем применять функцию pcntl_fork(). Она создаёт новейший процесс (дочерний), тот, что является фактически полной копией процесса-родителя, исполняющего данный вызов.
Позже вызова pcntl_fork() алгорифм разветвляется: в случае удачного выполнения функции pcntl_fork()она возвращает PID дочернего процесса родительскому, а NULL дочернему. Если создание форка закончилось неудачей, функция pcntl_fork() возвращает значение = null; $childs = array(); for ($i=0; $i<5; $i ) { $pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); //создаём связанные сокеты $pid = pcntl_fork(); //создаём форк if ($pid == -1) { die(“error: pcntl_fork”); } elseif ($pid) { //родительский процесс fclose($pair[0]); //закрываем один из сокетов в родителе $childs[] = $pair[1]; //второй будем применять для связи с потомком } else { //дочерний процесс fclose($pair[1]); //закрываем 2-й из сокетов в потомке $parent = $pair[0]; //первый будем применять для связи с родителем break; //выходим из цикла, Дабы дочерние процессы создавались только из родителя } }
В итоге работы этого кода в родителе массив $childs будет содержать в себе все сокеты для связи с потомками, а потомки для связи с родителем будут применять $parent.

Распределение процессов на мастера и воркеров

Так как дочерние процессы в нашей реализации не связаны друг с ином напрямую и могут взаимодействовать только через родителя, то уместно распределение обязанностей между родителем и потомками:

  • родитель будет отвечать за взаимодействие между дочерними процессами (будет мастером)
  • дочерние процессы будут исполнять всю работу (будут воркерами)

Также воркер у нас будет заниматься пересылкой сообщений из скриптов со страниц сайта либо из крона. Для этого мы сотворим добавочный сокет, и добавим его в массив, прослушиваемых сокетов. Скажем, дозволено сделать unix-сокет:

$service = stream_socket_server('unix:///tmp/websocket.sock', $errorNumber, $errorString);

Проксирование вебсокетов с поддержкой nginx

Nginx поддерживает проксирование вебсокетов начиная с версии 1.3.13. Вследствие nginx дозволено обрабатывать соединения к серверу вебсокетов на том же порту, что и сайт, а также ограничить число открытых вебсокетов с одного ip и другие полюбившиеся вам плюшки.

Пример nginx-конфига, тот, что это разрешает:

limit_conn_zone $binary_remote_addr zone=perip:10m;

server {
    listen 5.135.163.218:80;
    server_name sharoid.ru;

    location / {
        limit_conn perip 5; #делаем лимитация 5 вебсокетов на 1 ip
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s; #увеличиваем таймаут для вебсокетов
    }
}

Запуск из консоли

Исполняем команду php websocket.php либо ./websocket.php (заранее дав права на выполнение)
Если применять nohup, скажем, nohup ./websocket.php &, то скрипт продолжит трудиться позже закрытия консоли.

По-умолчанию есть два ограничения числа соединений на один процесс.

  • Первое — на ярусе операционной системы: в одном процессе невозможно открыть больше чем 1024 соединения. Дабы обрабатывать огромнее одновременных соединений, исполните команду: ulimit -n 65535, а если у пользователя неудовлетворительно привилегий, то sudo sh -c "ulimit -n 65535 && exec su $LOGNAME". Нынешнее значение дозволено посмотреть применяя команду ulimit -n
  • Второе — у функции stream_select(): она не принимает огромнее чем 1024 соединения. Тут всё труднее — необходимо перекомпилировать php c увеличенным FD_SETSIZE

Как я теснее писал, эти ограничения дозволено обойти, применяя дочерние процессы (воркеры).

Интеграция с вашим фреймворком на примере yii

Так как наш мастер прослушивает добавочный сокет для связи с нашими скриптами (в примере выше былunix:///tmp/websocket.sock), мы можем в любом месте нашего сайта либо в кроне соединиться с этим сокетом и отправить сообщение, которое мастер разошлёт каждому воркерам, а они, в свою очередь, все заказчикам:

$service = stream_socket_client ('unix:///tmp/websocket.sock', $errno, $errstr);
fwrite($service, 'каждому здравствуй');

С применением компонента yii это будет выглядеть вот так:

Yii::app()->websocket->send('каждому здравствуй');

 

Подробнее для yii

Скачиваем экстеншн, кладём его в папку extensions/websocket
В папку components кладём Websocket.php, WebsocketMasterHandler.php и WebsocketWorkerHandler.php из папки sample/yii.
В папку commands кладём из WebsocketCommand.php из папки sample/yii.
В конфиги main.php и console.php вставляем в секцию components:

'websocket' => array(
    'class' => 'Websocket',
    //'websocket' => 'tcp://127.0.0.1:8000',
    //'localsocket' => 'tcp://127.0.0.1:8001',// unix:///tmp/mysock
    //'workers' => 1
),

В конфиг console.php также вставляем в секцию import:

'ext.websocket.*'

Демонстрация

Демонстрационный чат 2.0 (добавлен список пользователей, добавлено лимитация: 1 сообщение в секунду с одного IP)
В нём были использованы описанные выше функции, а также поправлены недочеты, выявленные позже публикации предыдущей статьи.
Демонстрационный чат 1.0 (без списка пользователей, без ограничений)

Все исходники я оформил в виде библиотеки и выложил на github

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB