[

пятница, 25 января 2013 г.

Установка и настройка phpdaemon на девственно чистом debian squeeze

Появилась необходимость поднять WebSocket сервер, а так как проет пишется на php, то тут особых вариантов и не было - phpdaemon. Немного погуглив нашел сравнительную статью Нагрузочное тестирование: Node.JS vs phpDaemon. Результаты порадовали и было решено попробовать это чудо (скажу только, что пока нареканий нет, la упал на порядок + разгрузили fcgi воркеров).

Установка Debian

Установочный образ debian-6.0.6-amd64-netinst.iso
При установке выбрал: Сервер SSH, Стандартные системные утилиты

Требования phpdaemon

Ставить будем из репо, по-этому необходим git:
# apt-get install git

Новый phpdaemon (от 23 января 2013) требует php 5.4. Поставить можно так (взято с serverfault http://serverfault.com/questions/404815/how-can-i-upgrade-php-to-a-higher-version-for-debian-squeeze):
# echo "deb http://ftp.ru.debian.org/debian/ wheezy main non-free contrib" >> /etc/apt/sources.list

# cat >> /etc/apt/preferences <<EOF
Package: *
Pin: release n=squeeze
Pin-Priority: 650

Package: *
Pin: release n=wheezy
Pin-Priority: -10
EOF

apt-get update
apt-get install -t wheezy php5-cli php5-dev php-pear

Из требований самого phpdaemon:
# apt-get install -t wheezy libevent-dev
# pecl install https://bitbucket.org/osmanov/pecl-event/downloads/event-1.0.0.tgz<
# echo "extension=event.so" > /etc/php5/conf.d/event.ini

# pecl install channel://pecl.php.net/proctitle-0.1.2
# echo "extension=proctitle.so" > /etc/php5/conf.d/proctitle.ini

# cd /usr/src/
# git clone https://github.com/zenovich/runkit.git
# cd runkit/
# pecl install package.xml
# echo "extension=runkit.so" > /etc/php5/conf.d/runkit.ini

# pecl install eio
# echo "extension=eio.so" > /etc/php5/conf.d/eio.ini

Стоит проверить наличие следующих модулей:
# php -m | grep pcntl
# php -m | grep shmop
# php -m | grep sockets

Установка phpdaemon

Репо на github: https://github.com/kakserpom/phpdaemon

Ставим:
# cd /opt/
# git clone https://github.com/kakserpom/phpdaemon.git

Настройка phpdaemon

Конфиги лежат в /opt/phpdaemon/conf/ и /opt/phpdaemon/conf/conf.d/, я предпочитаю их в /etc/phpdaemon/.

Копируем конфиги:
# mkdir /etc/phpdaemon
# cp /opt/phpdaemon/conf/phpd.conf.example /etc/phpdaemon/phpd.conf
# cp /opt/phpdaemon/conf/AppResolver.php /etc/phpdaemon/
# cp -rp /opt/phpdaemon/conf/conf.d /etc/phpdaemon/

AppResolver.php служит для роутинга приложений (но! не для WebSocket. для WS ws://server/RouteForAppName).

Правим основной конфиг /etc/phpdaemon/phpd.conf:
user www-data;
group www-data;
  
max-workers 1;
min-workers 1;
start-workers 1;
max-idle    0;
  
# logging-related
logevents       1;
logqueue        1;
logreads        1;
logsignals      1;
verbose         0;
verbosetty      0;
  
# apps search
appfilepath '{app-*,applications,/var/www/apps-phpdaemon}/%s.php';

# apps routing
path '/etc/phpdaemon/AppResolver.php';

# Pools
Pool:WebSocketServer {
    enable 1;
    listen '0.0.0.0';
    port 8047; 
}
  
Pool:HTTPServer {
    enable 1;
    listen '0.0.0.0';
    port 8080;
    expose 1;
}
  
# Apps
# для фоллбэка на http если клиент не поддерживает websocket 
WebSocketOverCOMET {
    enable 1;
}

# Для теста доступности по HTTP
Example {
    enable 1;
}

# Для теста WS с фолбэком на comet и longpolling
ExampleWebSocket {
    enable 1;
}

# прочие конфиги/приложения
#include conf.d/*.conf

Есть один нюанс: пул WebSocketServer должен быть в конфиге до пула HTTPServer, так как на данный момент при инициализации HTTPServer получает инстанс WebSocketServer. Если последнего нет, то он создается, но не пул а отдельный сервер. Если указано несколько воркеров, то только первый сможет сделать бинд для сокета WS.

appfilepath - параметр для поиска приложений. Наши приложения будут лежать в /var/www/apps-phpdaemon/. Параметр передается в glob() так:
if (class_exists('Daemon', false) && isset(Daemon::$config->appfilepath->value)) {
    $files = glob($g = sprintf(Daemon::$config->appfilepath->value, str_replace('_', DIRECTORY_SEPARATOR, $classname)), GLOB_BRACE);
    if (isset($files[0])) {
        require $files[0];
        return;
    }
}

Создадим папку для наших приложений:
# mkdir /var/www/apps-phpdaemon/

Поправим роутер приложений (/etc/phpdaemon/AppResolver.php), чтобы дать доступ к приложению ExampleWebSocket:
- if (preg_match('~^/(WebSocketOverCOMET|Example)/~', $req->attrs->server['DOCUMENT_URI'], $m)) {
+ if (preg_match('~^/(WebSocketOverCOMET|Example|ExampleWebSocket)/~', $req->attrs->server['DOCUMENT_URI'], $m)) {

Тестовый запуск

Запуск:
# php /opt/phpdaemon/bin/phpd start

В файле phpd в качестве интерпретатора указан /opt/bin/php, поэтому нужен запуск в виде php /opt/phpdaemon/bin/phpd.

Проверяем, что все "взлетело":
# ps aux | grep phpd
root     24225  0.5  1.3  95520  6968 ?        SNs  14:15   0:00 phpd: master process                                                                                                           
www-data 24226  0.3  1.4  96040  7272 ?        SN   14:15   0:00 phpd: IPC process                                                                                                              
www-data 24227  0.5  1.6  96592  8196 ?        SNl  14:15   0:00 phpd: worker process                                                                                                           
root     24230  0.0  0.1   7572   868 pts/0    S+   14:16   0:00 grep phpd

# netstat -ntpa | grep phpd
tcp        0      0 0.0.0.0:8047            0.0.0.0:*               LISTEN      24225/phpd: master 
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      24225/phpd: master 


Если теперь вбить в браузере http://server-address:8080/ExampleWebSocket/ то должны увидеть следующее.

Кнопка Create WebSocket инициирует соединение, Ping - отправляет запро, Close WebSocket - закрывает соединение.

Посмотреть пакеты WS (в хроме) можно щелкнув на соответствующий запрос на установку WS соединения.



Остановить phpdaemon можно так:
# php /opt/phpdaemon/bin/phpd stop

init скрипт (из репо не подходит для squeeze), ротация логов и прочее - на ваше усмотрение.

Постскриптум

Где-то по ходу пропустил зависимость и у меня поставился apache, хотя он и не нужен в данном случае и может только мешать, если HTTPServer phpdaemon будет слушать на 80м порту.