введение
Недавно разработал небольшую функцию, использовал очередь 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 через мастер-процесс, начиная с/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
После этого отправить в рабочий процессSIGKILL
Signal, по умолчанию процесс можно принудительно закрыть.
Рабочий процесс является ответвлением главного процесса, так что главный процесс может пройти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("/");
}
скопировать код
Всего шагов пять:
- Разветвите дочерний процесс, родительский процесс завершится.
- Установите дочерний процесс в качестве лидера сеанса и лидера процесса.
- Снова разветвление, родительский процесс завершается, а дочерний процесс продолжает работать.
- Маска файла восстановления
0
. - Измените текущий каталог на корневой каталог
/
.
Второй шаг - подготовка к первому шагу, установка процесса в качестве лидера сеанса, необходимое условие - процесс не является лидером процесса, поэтому делаем первую вилку, процесс-лидер (родительский процесс) выходит, а дочерний процесс проходит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 команд для этой библиотеки классов следующим образом:
- запустить стартовую команду
- перезапустить принудительно перезагрузить
- стоп плавный стоп
- перезагрузить изящный перезапуск
- бросить принудительно остановить
- статус Посмотреть статус процесса
команда запуска
Команда запуска является процессом по умолчанию. После процесса по умолчанию следует команда запуска. Команда запуска проверяет, есть ли уже 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 - Старый машинист будет радовать вас каждый день