digitalstars / daemon
Library for easy work and create PHP Daemons
Requires
- php: >=7.1
- ext-pcntl: *
- ext-posix: *
- ext-sysvmsg: *
README
Комьюнити:
Почему SimpleDaemon?
- Легковесность — Daemon не тянет за собой кучу тяжёлых библиотек, в зависимостях только модули PHP, необходимые для работы
- Универсальность и простота — Несмотря на свою легковесность и простоту, Daemon является универсальным и гибким решением, предоставляющим функционал, которого хватит в большинстве случаев
- Многопоточность — при всём при этом присутствует многопоточная обработка данных (только для Linux)
Функционал
В библиотеке содержится 2 модуля:
- Сервер — Обрабатывает какие-то данные, переданные клиентом, поддерживается также работа в многопоточном режиме (Только для Linux систем)
- Клиент — может быть подключен к любому файлу php для моментальной отправки данных серверу, и вызова методов сервера
Как это работает и зачем оно нужно
На сервере могут быть реализованы функции, которые требуют достаточно больших вычислений и ничего не возвращают. Клиент может быстро вызывать эти функции и передавать в них данные (если все процессы демона заняты, то вызовы будут копиться в очереди). При этом, после того как функция завершится на сервере, вернуть какое-то значение из неё клиенту обратно не возможно (из-за отсутствия в PHP асинхронности).
Типичный пример когда это может пригодится — логирование чего нибудь или сложные вычисления. Представим, что у вас есть веб сервер, который обрабатывает какие-нибудь запросы, и где-то среди этих запросов нужно провести вычисления сложной функции, а результат записать в базу данных (не возвращая в качестве ответа). В таком случае, вычисление значения и запись его в БД можно перенести в скрипт сервера демона, а внутри скрипта обрабатываемого веб сервером инициализировать клиент демона, который просто вызовет функцию с передачей необходимых параметров, и не дожидаясь её выполнения вернёт пользователю запрошенную информацию. Схематичный пример ниже
Оглавление
- Подключение
- Сервер
- Инициализация сервера
- Методы настройки сервера
- isLog($is = true) - перенаправление вывода в файл или игнорирование вывода
- isMultiThread() - включение многопоточности
- maxThreads(int $count) - максимальное число дочерних процессов
- maxSize(int $size) - установка максимального размера сообщения
- Основные методы сервера
- init($func) - инициализация сервера
- errorHandler($func) - обработка ошибок
- module($id, $func) - регистрация функции, которую может вызвать клиент (Основной функционал сервера демона тут)
- Служебные методы сервера - это крайние случаи, у всех этих методов есть аналогичные
консольные команды
- bool clear() - очистка очереди сообщений
- stop() - остановка сервера
- kill() - принудительная остановка сервера
- int isActive() - проверка, запущен ли сейчас демон
- run() - запуск демона
- Управление работой сервера - управление работой сервера демона через консольные команды
- Клиент
- Инициализация клиента
- Методы клиента
- errorHandler($func) - обработка ошибок клиента
- send($module_id, $msg = []) - вызов функций сервера и отправка параметров (Основной функционал клиента демона тут)
Подключение
Используя composer
- Установить
composer require digitalstars/daemon
- Подключить
autoload.php
require_once "vendor/autoload.php";
Вручную
- Скачать последний релиз c github
- Подключить
autoload.php
.
Вот так будет происходить подключение, если ваш скрипт находится в той же папке, что и папка
daemon-master
require_once "daemon-master/autoload.php";
Сервер
Сервер представляет собой отдельный PHP скрипт, в котором реализованы методы для обработки данных. Этот скрипт запускается и работает в фоновом режиме, не зависимо от других скриптов. Для управления состоянием сервера предусмотрены консольные команды. Одновременно может быть запущенно несколько серверов.
Клиент соединяется с сервером по ID при инициализации. Важно чтобы они совпадали!
Инициализация сервера
require_once "vendor/autoload.php"; // Подключаем файлы use DigitalStars\Daemon\Server; // Подключаем класс Server $s = new Server(9); // Инициализируем демона с ID = 9
Также для удобства поддерживается инициализация через метод Create
$s = Server::create(9);
Методы настройки сервера
Все методы настройки возвращают $this. По этому их можно вызывать цепочкой вызовов сразу после инициализации через метод Server::create()
Пример: \$s = Server::create(9)->isLog(false)->isMultiThread()->maxThreads(4);
isLog($is = true)
Метод для управления легированием. Если указать true
, то в директории скрипта создастся новая директория daemon_logs
, в которой 3 файла с логами:
- error.log - содержит ошибки, возникшие во время работы
- application.log - становится стандартным выводом (все сообщения, которые должны были выводится в консоль при нормальной работе скрипта будут в этом файле)
- daemon.log - тоже ошибки возникшие во время работы, но уже другой тип ошибок
Если выключить логирование, то вывод ошибок и прочих сообщений будет просто игнорироваться.
isMultiThread()
Метод активирует многопоточную обработку данных, активировать многопоточность можно только если в php присутствует
модуль ext-pcntl
. Если модуля нет, то будет сгенерировано исключение
maxThreads(int $count)
Устанавливает максимальное количество дочерних процессов, если многопоточность удалось включить.
maxSize(int $size)
Метод для установки максимального размера сообщения. Подробнее об этому можно почитать тут (параметр max_message_size).
Если не уверены, то лучше не трогать
Основные методы сервера
init($func)
Устанавливает функцию инициализации переменных, которые должны быть доступны во время обработки данных клиента. Внутри неё можно инициализировать, например, объект для работы с БД
Пример
use PDO; use DigitalStars\Daemon\Server; $s = Server::create(9); $db = null; $s->init(function () use (&$db) { $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'DB_USER', 'DB_PASSWORD'); });
errorHandler($func)
Устанавливает функцию для обработки ошибок. При этом, если функция задана, то исключение не будет выкинуто. Информация об ошибках передаётся в функцию в следующем виде:
$func($error, $message, $file, $line, $e)
Где:
- $error - Код ошибки, может быть:
ERROR_DAEMON
- Внутренняя ошибка демона (если не найден какой-то модуль php или не удалось что-то выполнить)ERROR_MODULE
- Ошибка внутри модуля- Типы ошибок из PHP (
E_ERROR
,E_WARNING
,E_PARSE
,E_NOTICE
,E_CORE_ERROR
,E_CORE_WARNING
,E_COMPILE_ERROR
,E_COMPILE_WARNING
,E_USER_ERROR
,E_USER_WARNING
,E_USER_NOTICE
,E_STRICT
,E_RECOVERABLE_ERROR
,E_DEPRECATED
,E_USER_DEPRECATED
)
- $message - сообщение ошибки
- $file - файл в котором произошла ошибка
- $line - строка
- $e - Экземпляр Throwable (родитель Exception), описывающий ошибку
Внимание! $file, $line, $e будут присутствовать только в том случае, если тип ошибки не
ERROR_DAEMON
, иначе будет просто текстовое описание ошибки на русском языке.
Пример
use PDO; use DigitalStars\Daemon\Server; $s = Server::create(9); $db = null; $s->init(function () use (&$db) { $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'DB_USER', 'DB_PASSWORD'); }); $s->errorHandler(function ($error, $error_str, $error_file, $error_line, $e) use ($db) { // Составляем текст ошибки $text = "Error: $error\n" . "Str: $error_str" . "File: $error_file ($error_line)\n" . (($e instanceof Throwable) ? "Back Trace:" . $e->getTraceAsString() : ""); // Записываем ошибку в таблицу логов в БД $db->prepare("INSERT INTO error_log (text_error) VALUE (?)")->execute([$text]); });
module($id, $func)
Метод регистрирует новую функцию, которую можно будет вызвать из клиента по ID (первый параметр), и при необходимости передать в неё нужные данные
По сути этот метод - это основной функционал демона.
Пример
Код сервера:
use PDO; use DigitalStars\Daemon\Server; $s = Server::create(9); $db = null; $s->init(function () use (&$db) { $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'DB_USER', 'DB_PASSWORD'); }); $s->module('testFunction', function ($text) use (&$db) { $db->prepare("INSERT INTO log_action (text_value) VALUE (?)")->execute([$text]); });
Код клиента:
use DigitalStars\Daemon\Client; $c = Client::create(9); $c->testFunction('Какое-то тестовое сообщение, которое передастся серверу');
В этом примере клиент отправляет сообщение на сервер демона, он это сообщение принимает и записывает в БД
Служебные методы сервера
Эти методы в основном использовать не нужно. Они вызываются сами внутри библиотеки, но могут пригодиться при обработке каких-то крайних случаев
bool clear()
Метод для очистки очереди сообщений. По умолчанию сообщения от клиентов до сервера накапливаются в очереди даже если сервер не запущен. Этот метод очищает эту очередь и возвращает в случае успеха true, в случае ошибки false
stop()
Метод останавливает работу демона нормально. Это значит, что при вызове метода демон будет ждать завершения всех дочерних процессов, и как только они завершаться, работа будет завершена
kill()
Метод завершает работу демона аварийно. Даже если какой-то из дочерних процессов не завершены, они будут завершены принудительно
int isActive()
Проверяет, запущен ли демон. Если да, то вернёт PID главного (родительского) процесса. Если нет, то вернёт false.
run()
Запускает работу демона
Управление работой сервера
Обычно методы из секции Служебные методы
не используются, а управлять состоянием демона можно непосредственно из
консоли. Демон поддерживает следующие консольные команды:
start
- запустить демонаstop
- завершить работу демонаkill
- завершить работу демона принудительно (не завершённые процессы будут убиты)status
- получить статус (запущен демон сейчас или нет)restart
- перезапустить демона (при этом будет использоватьсяstop
для завершения работы). Может быть полезно при правках в код серверclear
- очистить очередь сообщений
Пример
Создадим скрипт сервера демона под названием test_daemon.php
со следующим содержимым:
use PDO; use DigitalStars\Daemon\Server; $s = Server::create(9)->isMultiThread()->maxThreads(2); $db = null; $s->init(function () use (&$db) { $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'DB_USER', 'DB_PASSWORD'); }); $s->module('testFunction', function ($text) use (&$db) { $db->prepare("INSERT INTO log_action (text_value) VALUE (?)")->execute([$text]); });
Чтобы заставить его работать, нужно выполнить команду: php test_daemon.php start
.
Для завершения работы нужно вызвать команду: php test_daemon.php stop
Клиент
Клиент может быть инициализирован в любом php скрипте, с помощью экземпляра клиента можно вызывать функции реализованные
в модуле сервера с передачей в них параметров. Параметрами могут быть любые атомарные переменные, массивы и некоторые
объекты (подробнее об этом можно почитать тут, параметр message
, serialize
задан в true
)
Клиент соединяется с сервером по ID при инициализации. Важно чтобы они совпадали!
Инициализация клиента
require_once "vendor/autoload.php"; // Подключаем файлы use DigitalStars\Daemon\Client; // Подключаем класс Server $c = new Client(9); // Инициализируем клиент демона с ID = 9 (ID должен совпадать у клиента и сервера)
Также для удобства поддерживается инициализация через метод Create
$c = Client::create(9);
Методы клиента
errorHandler($func)
Метод обработки ошибок клиента. Он упрощён если сравнивать его с аналогичным методом сервера. в функцию $func
просто
передаётся экземпляр ошибки Exception
. При этом, если функция задана, то исключение не будет выкинуто
send($module_id, $msg = [])
Метод вызывает функцию сервера с id = $module_id. При этом, в функцию будут переданы параметры из массива $msg.
Этот метод также реализован в виде магического метода. То есть, можно вызывать несуществующие методы клиента, которые реализованы на сервере, и тогда клиент вызовет соответствующий метод на сервере с передачей в него параметров.
Пример
Код сервера:
use PDO; use DigitalStars\Daemon\Server; $s = Server::create(9); $db = null; $s->init(function () use (&$db) { $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'DB_USER', 'DB_PASSWORD'); }); $s->module('testFunctionTwoArguments', function ($val1, $val2) use (&$db) { $db->prepare("INSERT INTO log_action_two_field (field_1, field_2) VALUE (?, ?)")->execute([$val1, $val2]); }); $s->module('testFunctionOneArguments', function ($text) use (&$db) { $db->prepare("INSERT INTO log_action (text_value) VALUE (?)")->execute([$text]); });
Код клиента:
use DigitalStars\Daemon\Client; $c = Client::create(9); // Следующие 2 строки равносильны. Они вызовут функцию testFunctionTwoArguments на сервере и передадут в неё 2 параметра $c->testFunctionTwoArguments('Значение 1', 'Значение 2'); $c->send('testFunctionTwoArguments', ['Значение 1', 'Значение 2']); // Следующие 3 строки равносильны. Они вызовут функцию testFunctionOneArguments на сервере и передадут в неё 1 параметр $c->testFunctionOneArguments('Одно значение'); $c->send('testFunctionOneArguments', ['Одно значение']); $c->send('testFunctionOneArguments', 'Одно значение');
В этом примере клиент отправляет сообщение на сервер демона, он это сообщение принимает и записывает в БД