Модель процессов и жизненный цикл

Localzet Server использует многопроцессную архитектуру Master-Worker для обеспечения высокой производительности и отказоустойчивости.

Жизненный цикл Master Process

Фаза 1: Инициализация (Initialization)

Мастер-процесс начинает работу с последовательной инициализации всех компонентов системы:

Server::runAll()
├── checkSapiEnv()        // Проверка окружения
├── initStdOut()          // Инициализация потоков вывода
├── init()                // Настройка событий и обработчиков
├── parseCommand()        // Разбор командной строки
├── checkPortAvailable()  // Проверка доступности портов
├── lock()                // Файловая блокировка
├── daemonize()           // Переход в режим демона (опционально)
├── initServers()         // Инициализация серверов
├── installSignal()       // Установка обработчиков сигналов
├── saveMasterPid()       // Сохранение PID мастера
└── displayUI()           // Отображение интерфейса

Детали инициализации

Проверка окружения (checkSapiEnv)

Мастер-процесс проверяет:

  • Запуск из CLI или Micro SAPI
  • Наличие необходимых расширений PHP (pcntl, posix для Unix)
  • Доступность критических функций PHP (не должны быть в disable_functions)

Демонизация (daemonize)

Процесс демонизации включает двойной fork для полного отключения от терминала:

1. Первый fork():
   - Родительский процесс завершается (exit 0)
   - Дочерний процесс продолжает работу

2. posix_setsid():
   - Создание новой сессии
   - Отключение от управляющего терминала

3. Второй fork():
   - Предотвращает восстановление связи с терминалом
   - Финальный процесс становится демоном

Установка сигналов (installSignal)

Мастер-процесс регистрирует обработчики для следующих сигналов:

СигналДействиеОписание
SIGINTОстановкаНемедленная остановка всех процессов
SIGTERMОстановкаОстановка с ожиданием завершения
SIGQUITGraceful StopПлавная остановка с закрытием соединений
SIGUSR1ReloadГорячая перезагрузка кода
SIGUSR2Graceful ReloadПлавная перезагрузка
SIGHUPПерезапускПолный перезапуск сервера
SIGIOTСтатистикаЗапрос статистики от worker процессов
SIGIOСоединенияЗапрос информации о соединениях

Фаза 2: Создание Worker Processes

После инициализации мастер создает дочерние процессы:

forkServers()
├── Для каждого сервера:
│   ├── Определение количества worker процессов ($server->count)
│   └── while (count < required):
│       └── forkOneServerForLinux()
│           ├── pcntl_fork()
│           ├── В родителе: сохранение PID
│           └── В дочернем: инициализация worker

Механизм форкинга:

// Родительский процесс (Master)
if ($pid > 0) {
    // Сохраняем PID нового worker
    static::$pidMap[$serverId][$pid] = $pid;
    static::$idMap[$serverId][$id] = $pid;
    
    // Продолжаем создавать другие процессы
}

// Дочерний процесс (Worker)
elseif ($pid === 0) {
    // 1. Очистка состояния
    static::$pidsToRestart = static::$pidMap = [];
    
    // 2. Закрытие других серверов (worker обслуживает только один сервер)
    foreach (static::$servers as $key => $oneServer) {
        if ($oneServer->serverId !== $server->serverId) {
            $oneServer->unlisten();
            unset(static::$servers[$key]);
        }
    }
    
    // 3. Инициализация Event Loop
    static::$globalEvent = new EventLoopClass();
    
    // 4. Настройка пользователя процесса
    $server->setUserAndGroup();
    
    // 5. Запуск сервера
    $server->run();
    
    // 6. Запуск Event Loop (блокирующий вызов)
    static::$globalEvent->run();
}

Фаза 3: Мониторинг (Monitoring)

Мастер-процесс переходит в режим мониторинга:

monitorServers()
├── Бесконечный цикл:
│   ├── Проверка статуса worker процессов
│   ├── Обработка сигналов
│   ├── Перезапуск упавших процессов
│   └── Graceful reload (если запрошен)

Мониторинг процессов:

while (true) {
    // Для каждого worker процесса
    foreach (static::$pidMap as $serverId => $pids) {
        foreach ($pids as $pid) {
            // Проверка существования процесса
            if (!posix_kill($pid, 0)) {
                // Процесс завершен - пересоздаем
                static::forkOneServerForLinux($server);
            }
        }
    }
    
    // Обработка сигналов (через signal handler)
    pcntl_signal_dispatch();
    
    // Пауза перед следующей проверкой
    usleep(10000); // 10ms
}

Жизненный цикл Worker Process

Инициализация Worker

После форкинга worker процесс проходит следующую инициализацию:

  1. Очистка состояния: Удаление ссылок на другие серверы и процессы
  2. Создание Event Loop: Инициализация событийного цикла для данного процесса
  3. Установка обработчиков: Регистрация callback-функций для событий
  4. Привязка к сокету: Начало прослушивания входящих соединений
  5. Запуск Event Loop: Переход в асинхронный режим работы

Работа с соединениями

Принятие соединения:

1. Event Loop обнаруживает событие "readable" на listening socket
2. Вызывается Server::acceptTcpConnection()
3. Создается объект TcpConnection
4. Регистрируется callback для чтения данных
5. Вызывается onConnect callback (если установлен)

Обработка данных:

1. Event Loop обнаруживает данные на клиентском сокете
2. TcpConnection::baseRead() читает данные в recvBuffer
3. Протокол прикладного уровня проверяет целостность пакета (input)
4. Когда пакет полный, вызывается decode()
5. Декодированные данные передаются в onMessage callback
6. Бизнес-логика обрабатывает запрос
7. Ответ кодируется протоколом (encode)
8. Данные отправляются клиенту через send()

Управление памятью Worker

Буферизация:

  • recvBuffer: Буфер приема необработанных данных
  • sendBuffer: Буфер отправки, ожидающий записи
  • currentPackageLength: Текущая длина ожидаемого пакета

Кеширование:

// Кеш запросов (для оптимизации парсинга)
static $requests = [];

// Кеш применяется для:
// 1. Повторяющихся HTTP запросов
// 2. Избежания повторного парсинга идентичных данных
// 3. Оптимизации использования памяти

Состояния процессов

Состояния Master Process

СостояниеЗначениеОписание
STATUS_INITIAL (0)ИнициализацияНачальное состояние перед запуском
STATUS_STARTING (1)ЗапускСоздание worker процессов
STATUS_RUNNING (2)РаботаНормальная работа, мониторинг workers
STATUS_SHUTDOWN (4)ОстановкаПроцесс остановки
STATUS_RELOADING (8)ПерезагрузкаПлавная перезагрузка

Состояния Worker Process

Worker процесс имеет аналогичные состояния, но работает независимо от мастера после запуска.

Состояния соединения (TcpConnection)

СостояниеЗначениеОписание
STATUS_INITIAL (0)ИнициализацияСоздание объекта
STATUS_CONNECTING (1)ПодключениеУстановка соединения
STATUS_ESTABLISHED (2)УстановленоАктивное соединение
STATUS_CLOSING (4)ЗакрытиеПроцесс закрытия
STATUS_CLOSED (8)ЗакрытоСоединение закрыто

Управление жизненным циклом

Остановка сервера

Немедленная остановка (SIGINT / SIGTERM):

1. Мастер получает сигнал
2. Отправка SIGTERM всем worker процессам
3. Ожидание завершения (timeout = stopTimeout)
4. Принудительное завершение (SIGKILL) если timeout
5. Очистка ресурсов и выход

Плавная остановка (SIGQUIT):

1. Мастер получает SIGQUIT
2. Установка флага gracefulStop = true
3. Отправка SIGQUIT всем worker процессам
4. Worker процессы:
   - Прекращают принимать новые соединения
   - Закрывают пустые соединения
   - Ожидают завершения обработки активных запросов
   - Закрывают соединения после обработки
5. По завершении всех соединений - выход

Перезагрузка кода

Горячая перезагрузка (SIGUSR1):

1. Мастер получает SIGUSR1
2. Отправка SIGUSR1 всем worker процессам
3. Worker процесс:
   - Завершает текущую обработку
   - Перезагружает PHP файлы (autoload)
   - Сохраняет активные соединения
   - Продолжает работу с новым кодом

Плавная перезагрузка (SIGUSR2):

1. Мастер получает SIGUSR2
2. Для каждого worker процесса последовательно:
   a. Создание нового worker с новым кодом
   b. Ожидание завершения старого worker
   c. Переход к следующему worker
3. Все соединения плавно переносятся на новые процессы

Межпроцессное взаимодействие

Обмен данными через файлы

Статистика:

  • Путь: $statusFile
  • Формат: Сериализованные данные
  • Обновление: По сигналу SIGIOT

Соединения:

  • Путь: $connectionsFile
  • Формат: Сериализованные данные о соединениях
  • Обновление: По сигналу SIGIO

PID-файлы

  • $pidFile: PID мастера
  • Используется для проверки запущенности сервера
  • Блокировка предотвращает множественный запуск

Изоляция и безопасность

Изоляция памяти

Каждый worker процесс имеет полностью изолированную память:

  • Отдельное адресное пространство
  • Независимые глобальные переменные
  • Изолированные статические данные
  • Независимые подключения к БД

Управление привилегиями

Worker процессы могут работать от непривилегированного пользователя:

$server->user = 'www-data';
$server->group = 'www-data';

// После привязки к порту:
posix_setgid($gid);
posix_initgroups($user, $gid);
posix_setuid($uid);

Это обеспечивает:

  • Минимальные привилегии (principle of least privilege)
  • Безопасность при компрометации процесса
  • Соответствие требованиям безопасности

Событийные циклы и асинхронность