понедельник, 4 июля 2016 г.

Использование PHPDAEMON в CentOS7

Подготовка к установке

До установки демона необходимо обновиться до php 5.6
Обновление PHP будет производиться из репозитория Remi, поэтому произведем его подключение.
# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
Редактируем файл репозитария /etc/yum.repos.d/remi.repo
# nano /etc/yum.repos.d/remi.repo
В секции
[remi-php56]
ставим
enabled=1
Затем устанавливаем новую версию php:
# yum install php -y
# systemctl restart httpd
Проверить версию php можно так:
# php -v
PHP 5.6.23 (cli) (built: Jun 22 2016 08:56:52)

Далее нужно поставить свежую версию библиотеки libevent 2
# cd /usr/src/
# yum install gcc kernel-devel make ncurses-devel
# curl http://sourceforge.net/projects/levent/files/latest/download?source=files -L -o libevent2.tar.gz -w 'Last URL was: %{url_effective}'
# curl http://sourceforge.net/projects/tmux/files/latest/download?source=files -L -o tmux.tar.gz -w 'Last URL was: %{url_effective}'
# tar zxvf libevent2.tar.gz
# cd libevent-2.0.22-stable
# ./configure --prefix=/usr/local
Библиотеку устанавливаем в папку/usr/local
# make
# make install

Ставим необходимые в дальнейшем пакеты и модули php:
# yum install -y git gcc openssl-devel
# yum install php-pear
# yum install php-devel
# pecl install eio inotify
# pecl install http://pecl.php.net/get/event
На вопросы, задаваемые инсталлятором отвечаем так:
libevent installation prefix [/usr] : /usr/local
Include libevent's pthreads library and enable thread safety support in Event [no] : yes
На остальные вопросы можно ответить значением по умолчанию просто нажав Enter.

Для корректной работы модулей php event и eio необходим модуль sockets.
В системах RedHat/CentOS файлы конфигурации подгружаются в алфавитном порядке, поэтому назовем их z-event.ini и z-eio.ini соответственно и проведем подключение:
# echo "extension=event.so" > /etc/php.d/z-event.ini
# echo "extension=eio.so" > /etc/php.d/z-eio.ini

Установка phpdaemon

Готовим директорию, где будет работать демон phpdaemon и устанавливаемся:
#  mkdir /opt/phpdaemon
# cd /opt/phpdaemon
# git clone https://github.com/kakserpom/phpdaemon.git ./
Копируем конфигурационный файл из примера.  
# cp /opt/phpdaemon/conf/phpd.conf.example /opt/phpdaemon/conf/phpd.conf
Создаем символьную ссылку
# ln -s /opt/phpdaemon/bin/phpd /usr/bin/phpd

Устанавливаем временную зону в файле php.ini
Открываем файл:
# nano  /etc/php.ini
Прописываем в переменную date.timezone  - Московское время
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Etc/GMT-3

Запуск phpdaemon

Запускаем демон таким образом:
# phpd start --verbose-tty=1
Опция --verbose-tty=1 указыват демону выводить журнал в консоль.
Нормальный вывод сейчас должен выглядеть так:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
#

В дальнейшем работаем с демоном так:
# phpd start Запуск демона
# phpd stop Остановка демона
# phpd reload, phpd restart Перезапуск демона
# phpd status Статус демона
# phpd fullstatus Статус демона подробный

Логи работы сервера будут складываться по умолчанию в файл /var/log/phpdaemon.log.

Добавляем phpdaemon в автозапуск. Для этого создаем сервис systemd.
# touch /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
# chmod 664 /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
# nano /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
Содержание файла:
[Unit]
Description=PHP DAEMON START
After=network.target httpd.target

[Service]
Type=forking
User=root
Group=root
ExecStart=/usr/bin/php -f /opt/phpdaemon/bin/phpd start &

[Install]
WantedBy=multi-user.target 
Рестартуем демон systemd:
# systemctl daemon-reload
Теперь запустить демон phpdaemon можно и так:
# systemctl start phpdaemonstart.service

Phpdaemon и серверы HTTP и WebSocket

Включаем websocket сервер phpdaemon
# nano /opt/phpdaemon/conf/conf.d/WebSocketServer.conf
Содержимое файла приводим к виду:
Pool:WebSocketServer {
        # you can redefine default settings here
        privileged;
        listen 'tcp://0.0.0.0';
        port 8021;
}

Включаем web-сервер
# nano /opt/phpdaemon/conf/conf.d/HTTPServer.conf
Содержимое файла приводим к виду:
Pool:HTTPServer {
        #privileged;
        # you can redefine default settings here
        enable 1;
        listen '0.0.0.0';
        port 8080;
        expose 1;
}

Перезапускаем демон:
# phpd stop
# phpd start --verbose-tty=1
Вывод загрузки должен быть такой:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
[PHPD] M#23145 \PHPDaemon\Core\Pool:HTTPServer up.
[PHPD] ClassFinder: 'HTTPServer' -> '\PHPDaemon\Servers\HTTP\Pool', you should change your code.
[PHPD] M#23145 \PHPDaemon\Core\Pool:WebSocketServer up.
[PHPD] ClassFinder: 'WebSocketServer' -> '\PHPDaemon\Servers\WebSocket\Pool', you should change your code.
[root@callpeg ~]# [PHPD] M#23145 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\WebSocket\Pool up.
[PHPD] W#23149 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23152 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23148 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23150 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23155 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23147 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23151 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23156 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
Проверяем, что демон стартовал:
# ps ax | grep phpd
23307 ?        SNs    0:00 phpd: master process
23308 ?        SN     0:00 phpd: IPC process
23309 ?        SNl    0:00 phpd: worker process
23310 ?        SNl    0:00 phpd: worker process
23311 ?        SNl    0:00 phpd: worker process
23312 ?        SNl    0:00 phpd: worker process
23313 ?        SNl    0:00 phpd: worker process
23315 ?        SNl    0:00 phpd: worker process
23317 ?        SNl    0:00 phpd: worker process
23318 ?        SNl    0:00 phpd: worker process
23326 pts/1    S+     0:00 grep --color=auto phpd
Проверяем, слушаются ли порты 8021 (websoket-сервер от phpdaemon) и 8080 (web-сервер от phpdaemon)
# netstat -ltupn | grep phpd
tcp        0      0 0.0.0.0:8080   0.0.0.0:*  LISTEN   19297/phpd: master
tcp        0      0 0.0.0.0:8021   0.0.0.0:*  LISTEN   19297/phpd: master
Для возможности прослушивания портов по сети, открываем эти порты в iptables
# nano /etc/sysconfig/iptables
-A INPUT -p tcp --dport 8021 -j ACCEPT
-A INPUT -p tcp --dport 8080 -j ACCEPT
# systemctl restart  iptables

Включим в конфигурационном файле phpd.conf работу двух приложений, идущих в комплекте с пакетом phpdaemon,
# nano /opt/phpdaemon/conf/phpd.conf
Приводим файл к виду
## Config file

user root;
group root;

#Логирорвание
logging false;
log-storage '/var/log/phpdaemon.log';

max-workers     8;
min-workers     8;
start-workers   8;
max-idle        0;
#add-include-path '/path/to/your/folder';

# apps routing
path '/opt/phpdaemon/conf/AppResolver.php';

ExampleWebSocket {
}

ServerStatus {
}

include conf.d/*.conf;
Здесь указывается, что демон будет работать из-под пользователя root.
Файл описания маршрутизации запросов будет находиться здесь: /opt/phpdaemon/conf/AppResolver.php
Включены приложения ExampleWebSocket и ServerStatus
Логирование выключено.

Все приложения демона должны быть размещены в папке /opt/phpdaemon/PHPDaemon/Applications/.
Приложение ServerStatus в этой папке уже есть, а приложение ExampleWebSocket нужно скопировать туда из папки примеров:
# cp /opt/phpdaemon/Examples/ExampleWebSocket.php /opt/phpdaemon/PHPDaemon/Applications/ExampleWebSocket.php
Что бы этот пример заработал необходимо в файл внести небольшие изменения:
1. В самом начале файла строку
namespace PHPDaemon\Examples;
заменить на:
namespace PHPDaemon\Applications;
2. Строку
ws = new WebSocket('ws://' + document.domain + ':8047/exampleApp');
заменить на 
ws = new WebSocket('ws://' + document.domain + ':8021/exampleApp');

Для корректной отработки запросов к web-серверу на созданные приложения, необходимо внести изменения в конфигурационный файл маршрутизации AppResolver.php
# nano /opt/phpdaemon/conf/AppResolver.php
Функция getRequestRoute в этом файле описывает какие приложения будут отрабатывать при поступлении запросов к серверу из браузера клиента.
К имеющемуся условию:
if (preg_match('~^/(WebSocketOverCOMET|Example.*)/?~', $req->attrs->server['DOCUMENT_URI'], $m)) {
      return $m[1];
}
Добавляем условие:
if (preg_match('~^/(ServerStatus)/?~', $req->attrs->server['DOCUMENT_URI'], $m)) {
       return 'ServerStatus';
}

Перезапускаем демон:
# phpd stop
# phpd start --verbose-tty=1
Лог запуска теперь будет такой:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
[PHPD] M#23255 \PHPDaemon\Core\Pool:HTTPServer up.
[PHPD] ClassFinder: 'HTTPServer' -> '\PHPDaemon\Servers\HTTP\Pool', you should change your code.
[PHPD] M#23255 \PHPDaemon\Core\Pool:WebSocketServer up.
[PHPD] ClassFinder: 'WebSocketServer' -> '\PHPDaemon\Servers\WebSocket\Pool', you should change your code.
[PHPD] M#23255 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\WebSocket\Pool up.
[root@callpeg ~]# [PHPD] W#23258 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23258 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23258 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23257 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23257 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23257 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23261 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23261 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23261 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23259 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23259 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23259 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23262 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23260 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23262 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23262 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23260 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23260 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23263 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23263 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23263 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23264 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23264 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23264 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.

Теперь при обращении к серверу по адресу http://<ip-сервера>:8080/ServerStatus получим ответ о статусе сервера:


А при обращении к ресурсу http:// <ip-сервера>:8080/ExampleWebSocket  получим пример работы приложения на web-soket:


Первой кнопкой можно открыть сокет, второй кнопкой посылается запрос «ping» серверу, на который тот отвечает словом «pong». Третья кнопка служит для закрытия сокета.



В логах сервера phpdaemon, которые валять в консоль сервера мы увидим примерно следующее:
[PHPD] Notice: Undefined index: waitinit in /opt/phpdaemon/PHPDaemon/Applications/ServerStatusRequest.php:48
#1  PHPDaemon\Core\Daemon::errorHandler() called at [/opt/phpdaemon/PHPDaemon/Applications/ServerStatusRequest.php:48]
#2  PHPDaemon\Applications\ServerStatusRequest->run() called at [/opt/phpdaemon/PHPDaemon/Request/Generic.php:215]
#3  PHPDaemon\Request\Generic->eventCall()
#4  EventBase->dispatch() called at [/opt/phpdaemon/PHPDaemon/Core/EventLoop.php:149]
#5  PHPDaemon\Core\EventLoop->run() called at [/opt/phpdaemon/PHPDaemon/Thread/Worker.php:215]
#6  PHPDaemon\Thread\Worker->run() called at [/opt/phpdaemon/PHPDaemon/Thread/Generic.php:146]
#7  PHPDaemon\Thread\Generic->__invoke() called at [/opt/phpdaemon/bin/phpd:71]

[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.

1 комментарий:

  1. Спасибо. Очень доступно и понятно.
    Получилось настроить, хоть и были трудности.

    ОтветитьУдалить