Многопроцессорная очередь потребления PHP

задняя часть PHP сервер Nginx

введение

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

Этот метод должен модифицировать crontab каждый раз, если процесс завис, он не запустится вовремя, и он не запустится до следующего запуска crontab. При закрытии (перезапуске) процесса используется kill, что может привести к потере обрабатываемых данных.Например, в следующем примере мы предполагаем, что спящий процесс является логикой обработки.Чтобы наглядно увидеть эффект, время обработки увеличивается до 10 с:

<?php
$i = 1;
while (1) {
    echo "开始第[{$i}]次循环\n";
    sleep(10);
    echo "结束第[{$i}]次循环\n";
    $i++;
}скопировать код

После запуска скрипта ждем пока запустится цикл и отправляем процессkill {$pid}, по умолчанию отправляется число 15SIGTERMСигнал. Предположение$iОн был получен из очереди.Когда я получил 2, он был в обработке.Мы отправили сигнал уничтожения в программу.Как и потеря данных очереди,проблема относительно большая,поэтому я должен найти способ решить эти проблемы .

开始第[1]次循环
结束第[1]次循环
开始第[2]次循环


[1]    28372 terminated  php t.phpскопировать код

модель процесса nginx

В это время я подумал о nginx. Являясь основой высокопроизводительных серверов, nginx обслуживает тысячи предприятий и частных лиц. Его модель процесса относительно классическая, как показано ниже:

nginx 进程模型 图片来自网络

Администратор взаимодействует с nginx через мастер-процесс, начиная с/path/to/nginx.pidПрочитайте pid главного процесса nginx, отправьте сигнал главному процессу, мастер выполняет различную обработку в соответствии с разными сигналами, а затем возвращает информацию администратору. Рабочий процесс разветвляется мастер-процессом.Мастер отвечает за управление рабочим процессом и не занимается бизнесом.Рабочий процесс является обработчиком конкретного бизнеса.Мастер может контролировать выход и запуск рабочего процесса.При неожиданном выходе рабочего процесса , мастер получит выход из дочернего процесса.Сообщение также перезапустит новый рабочий процесс, чтобы дополнить его, чтобы бизнес-процессы не пострадали. nginx также может плавно завершить работу без потери обрабатываемых данных.При обновлении конфигурации nginx может загрузить новую конфигурацию, не влияя на онлайн-сервис, что особенно полезно при большом объеме запросов.

Разработка процесса

Глядя на модель nginx, мы можем полностью разработать аналогичную библиотеку классов для удовлетворения потребностей обработки данных mcq, чтобы один файл мог управлять всеми процессами, мог плавно завершаться и мог просматривать состояние дочерних процессов. Это не должно быть слишком сложным, потому что мы имеем дело с определенной задержкой в ​​​​получении данных очереди, и добиться бесперебойного обслуживания, такого как nginx, хлопотно, что требует много времени и труда, и не делает многого. смысл. Разработанная модель процесса похожа на nginx, больше похожа на упрощенную версию nginx.
多进程模型

Проект семафора процесса

Семафор — это способ межпроцессного взаимодействия, он относительно прост и имеет слабую единственную функцию, он может только посылать сигналы процессам, а процессы выполняют различную обработку в соответствии с сигналами.

Сохраните pid в файл при запуске главного процесса./path/to/daeminze.pid, администратор общается с главным процессом посредством сигналов.Главный процесс устанавливает три вида сигналов, сталкивается с разными сигналами и выполняет различную обработку, как показано ниже:

SIGINT  => 平滑退出,处理完正在处理的数据再退出
SIGTERM => 暴力退出,无论进程是否正在处理数据直接退出
SIGUSR1 => 查看进程状态,查看进程占用内存,运行时间等信息скопировать код

Главный процесс взаимодействует с рабочим процессом посредством сигналов, а рабочий процесс устанавливает два сигнала следующим образом:

SIGINT  => 平滑退出
SIGUSR1 => 查看worker进程自身状态скопировать код

Почему рабочий процесс устанавливает только 2 сигнала, одним меньшеSIGTERM, потому что главный процесс получил сигналSIGTERMПосле этого отправить в рабочий процессSIGKILLSignal, по умолчанию процесс можно принудительно закрыть.

Рабочий процесс является ответвлением главного процесса, так что главный процесс может пройтиpcntl_waitЧтобы дождаться события выхода дочернего процесса, когда дочерний процесс завершается, верните pid дочернего процесса, выполните обработку и запустите новый процесс, чтобы дополнить его.

Мастер-процесс также проходитpcntl_waitждать приема сигнала, а когда сигнал поступит, он вернется-1, в этом месте еще есть ямы, о которых будет подробно рассказано ниже.

В PHP есть 2 способа срабатывания сигнала, первый способdeclare(ticks = 1);, эта эффективность не высока, Zend каждый раз при выполнении низкоуровневого оператора будет проверять, есть ли в процессе необработанные сигналы, что сейчас редко используется,PHP 5.3.0и предыдущие версии могут использовать это.

Второй черезpcntl_signal_dispatchдля вызова необработанного сигнала,PHP 5.4.0и более поздние версии применимы, функция может быть разумно размещена в цикле, и в основном нет потери производительности.Рекомендуется применить сейчас.

Семафор восстановления установки PHP

PHP черезpcntl_signalДля установки сигнала объявление функции выглядит так:

bool pcntl_signal ( int $signo , [callback $handler [, bool $restart_syscalls = true ] )скопировать код

третий параметрrestart_syscallsЭто не очень просто понять.Я перерыл много информации, но не очень в ней разобрался.После тестирования выяснилось, что этот параметр правильный.pcntl_waitФункция приема сигнала имеет эффект, если установлено значение по умолчаниюtrueКогда сигнал отправлен, процесс используетpcntl_waitне получено, должно быть установлено значениеfalseПросто посмотрите на следующий пример:

<?php
$i = 0;
while ($i<5) {
    $pid = pcntl_fork();
    $random = rand(10, 50);
    if ($pid == 0) {
        sleep($random);
        exit();
    }
    echo "child {$pid} sleep {$random}\n";
    $i++;
}

pcntl_signal(SIGINT,  function($signo) {
     echo "Ctrl + C\n";
});

while (1) {
    $pid = pcntl_wait($status);
    var_dump($pid);
    pcntl_signal_dispatch();
}скопировать код

После запуска отправляем родительскому процессуkill -SIGINT {$pid}Signal обнаруживается, что pcntl_wait не отвечает, а при завершении дочернего процесса отправленныйSIGINTБудут выполняться один за другим, например, следующие результаты:

child 29643 sleep 48
child 29644 sleep 24
child 29645 sleep 37
child 29646 sleep 20
child 29647 sleep 31
int(29643)
Ctrl + C
Ctrl + C
Ctrl + C
Ctrl + C
int(29646)скопировать код

Это отправляется родительскому процессу четыре раза сразу после запуска скрипта.SIGINTСигналы, все сигналы будут срабатывать при запуске дочернего процесса.

Но при установке третьего параметра сигнала установки наfalse:

pcntl_signal(SIGINT,  function($signo) {
     echo "Ctrl + C\n";
}, false);скопировать код

В это время отправьте родительскому процессуSIGINTсигнал,pcntl_waitскоро вернется-1, также сработает событие, соответствующее сигналу.

Итак, третий параметр, вероятно, означает, перерегистрировать ли этот сигнал, если он ложный, он будет зарегистрирован только один раз и вернется после его срабатывания.pcntl_waitВы можете получить сообщение, если оно верно, то регистрация повторится и не вернется.pcntl_waitСообщение не получено.

Семафоры и системные вызовы

Семафор прервет системный вызов и позволит системному вызову вернуться немедленно, напримерsleep, когда процесс спит и получает сигнал, сон немедленно вернет оставшиеся секунды сна, например:

<?php
pcntl_signal(SIGINT,  function($signo) {
     echo "Ctrl + C\n";
}, false);

while (true) {
    pcntl_signal_dispatch();
    echo "123\n";
    $limit = sleep(2);
    echo "limit sleep [{$limit}] s\n";
}скопировать код

После запуска нажмитеCtrl + C, результат выглядит так:

123
^Climit sleep [1] s
Ctrl + C
123
limit sleep [0] s
123
^Climit sleep [1] s
Ctrl + C
123
^Climit sleep [2] sскопировать код

демон (демон) процесс

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

protected function daemonize()
{
    $pid = pcntl_fork();
    if (-1 == $pid) {
        throw new Exception("fork进程失败");
    } elseif ($pid != 0) {
        exit(0);
    }
    if (-1 == posix_setsid()) {
        throw new Exception("新建立session会话失败");
    }

    $pid = pcntl_fork();
    if (-1 == $pid) {
        throw new Exception("fork进程失败");
    } else if($pid != 0) {
        exit(0);
    }

    umask(0);
    chdir("/");
}скопировать код

Всего шагов пять:

  1. Разветвите дочерний процесс, родительский процесс завершится.
  2. Установите дочерний процесс в качестве лидера сеанса и лидера процесса.
  3. Снова разветвление, родительский процесс завершается, а дочерний процесс продолжает работать.
  4. Маска файла восстановления0.
  5. Измените текущий каталог на корневой каталог/.

Второй шаг - подготовка к первому шагу, установка процесса в качестве лидера сеанса, необходимое условие - процесс не является лидером процесса, поэтому делаем первую вилку, процесс-лидер (родительский процесс) выходит, а дочерний процесс проходитposix_setsid()Установить в качестве лидера сеанса, а также лидера процесса.

Третьим шагом является предотвращение восстановления контроля процесса над терминалом, поскольку необходимым условием для процесса контроля над терминалом является лидер сеанса (pid=sid).

Четвертый шаг — восстановить маску файла по умолчанию и избежать установки маски файла в предыдущей операции, что создает ненужные проблемы. По поводу маски файла, в linux маска файла будет использоваться при создании файлов и папок.Разрешение файла по умолчанию 666 и папки 777. При создании файла(папки) маска будет вычтена из значения по умолчанию Значение используется в качестве конечного значения для создания файла (папки), например маски022создать файл под666 - 222 = 644, создать папку777 - 022 = 755:

маска Новые права доступа к файлам Новые права доступа к папке
umask(0) 666 (-rw-rw-rw-) 777 (drwxrwxrwx)
umask(022) 644 (-rw-r--r--) 755 (drwxr-xr-x)

Пятый шаг — переключить текущий каталог на корневой каталог./, в Интернете говорят, что каталог, который избегает запуска, не может быть правильно удален, что не слишком хорошо понятно.

В соответствии с 5 шагами, различная информация об изменении идентификатора каждого шага:

после операции pid ppid pgid sid
Начинать 17723 31381 17723 31381
Первая вилка 17723 1 17723 31381
posix_setsid() 17740 1 17740 17740
вторая вилка 17840 1 17740 17740

Кроме того, взаимосвязь между сеансами, группами процессов и процессами показана на следующем рисунке, что полезно для лучшего понимания.
会话、进程组、进程关系

На этом этапе вы можете легко создать процесс демона.

дизайн команды

Я собираюсь разработать 6 команд для этой библиотеки классов следующим образом:

  1. запустить стартовую команду
  2. перезапустить принудительно перезагрузить
  3. стоп плавный стоп
  4. перезагрузить изящный перезапуск
  5. бросить принудительно остановить
  6. статус Посмотреть статус процесса

команда запуска

Команда запуска является процессом по умолчанию. После процесса по умолчанию следует команда запуска. Команда запуска проверяет, есть ли уже pid в файле pid, исправен ли процесс, соответствующий этому pid, и нужно ли его перезапускать. .

Команда принудительной остановки

Администратор отправляет главный процесс через файл записи, объединенный с pid.SIGTERMСигнал, который главный процесс отправляет всем дочерним процессамSIGKILLСигнал, после ожидания выхода всех рабочих процессов главный процесс также завершает работу.

Команда принудительного перезапуска

强制停止命令 + 启动命令

команда плавной остановки

Команда плавной остановки, которую администратор отправляет главному процессуSIGINTСигнал, который главный процесс отправляет всем дочерним процессамSIGINT, рабочий процесс помечает свое состояние какstoping, когда рабочий процесс зациклится в следующий раз, он будет основан наstopingРешите остановиться, чтобы не получать новые данные, и после выхода всех рабочих процессов главный процесс также завершается.

изящная команда перезапуска

平滑停止命令 + 启动命令

Посмотреть статус процесса

Проверьте статус процесса для этой ссылкиworkermanИдея, администратор отправляет мастер-процессSIGUSR1Сигнал, скажи основному процессу, я хочу видеть информацию всех процессов, мастер-процесс, мастер-процесс записывает информацию о своем процессе в настроенный путь к файлу А, а затем отправляетSIGUSR1, скажите рабочему процессу также записывать свою информацию в файл A. Поскольку этот процесс является асинхронным, неизвестно, когда рабочий процесс закончил запись, поэтому главный процесс ожидает здесь, и после того, как все рабочие процессы запишут файл, формат Вся информация на выходе преобразуется, и конечный результат выглядит следующим образом:

➜/dir /usr/local/bin/php DaemonMcn.php status
Daemon [DaemonMcn] 信息:
-------------------------------- master进程状态 --------------------------------
pid       占用内存       处理次数       开始时间                 运行时间
16343     0.75M          --             2018-05-15 09:42:45      0 天 0 时 3 分
12 slaver
-------------------------------- slaver进程状态 --------------------------------
任务task-mcq:
16345     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分
16346     0.75M          236            2018-05-15 09:42:45      0 天 0 时 3 分
--------------------------------------------------------------------------------
任务test-mcq:
16348     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16350     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16358     0.75M          49             2018-05-15 09:42:45      0 天 0 时 3 分
16449     0.75M          1              2018-05-15 09:46:40      0 天 0 时 0 分
--------------------------------------------------------------------------------скопировать код

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

Другие дизайны

Кроме того, были добавлены еще две практичные функции: одна — ограничение времени работы рабочего процесса, а другая — ограничение количества циклов обработки рабочего процесса для предотвращения непредвиденных ситуаций, таких как переполнение памяти в длительный петлевой процесс. Время по умолчанию — 1 час, а количество запусков по умолчанию — 10 раз.

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

код был размещенgithubЕсли интересно, можете попробовать, Windows не поддерживается, указывайте на ошибки.

开心专列-老司机带你开心每一天

Happy Train - Старый машинист будет радовать вас каждый день