_
| |
_ __ __ _ _ __ _ _| |_ ___
| '_ \ / _` | '__| | | | __/ _ \
| | | | (_| | | | |_| | || (_) |
|_| |_|\__,_|_| \__,_|\__\___/ .TIGERB.cn
An object-oriented multi process manager for PHP
Деловая сцена
В нашем реальном бизнес-сценарии (технологический стек PHP) нам может потребоваться выполнять некоторую бизнес-логику регулярно или почти в реальном времени.Проще говоря, мы можем использовать crontab, который поставляется с системой unix, для реализации запланированных задач, но для некоторых задач в реальном времени. требования выше Бизнес больше не применим, поэтому нам нужен инструмент управления задачами, который находится в памяти.Чтобы обеспечить производительность в реальном времени, с одной стороны, мы позволяем ему выполнять задачи все время (соответствующий сон, чтобы гарантировать, что процессор не занят на 100%), с другой стороны, мы реализуем многопроцессорность для обеспечения одновременного выполнения задач.
Цель
Подводя итог, моя цель - реализовать инструмент управления несколькими процессами master-worker на основе режима php-cli. Во-вторых, «у меня есть такая цель, как я ее анализирую, планирую и шаг за шагом реализую», что и является целью данной статьи.
Примечание. Далее родительский процесс совместно именуется главным, а дочерний процесс — рабочим.
анализировать
мы ставим этотРазделите большую цель на несколько маленьких целейЧтобы реализовать один за другим, следующим образом:
- мультипрогресс
- Назначение: одна основная вилка для нескольких рабочих
- Явление: идентификатор родительского процесса ppid всех рабочих процессов — это pid текущего мастера.
- мастер контролирует работника
- Цель: мастер уведомляет работника, и работник получает сообщение от мастера
- мастер получает сигнал
- Назначение: Мастер получает и настраивает обработку сигналов от терминала
мультипрогресс
Как разветвить процесс в PHPpcntl_fork
, Об этом должен знать каждый. Если вы не знаете, эту функцию будет легко найти, просто погуглив/бинг. Тогда FTM, посмотримpcntl_fork
Использование этой функции примерно так:
$pid = pcntl_fork(); // pcntl_fork 的返回值是一个int值
// 如果$pid=-1 fork进程失败
// 如果$pid=0 当前的上下文环境为worker
// 如果$pid>0 当前的上下文环境为master,这个pid就是fork的worker的pid
Затем посмотрите на код:
$pid = pcntl_fork();
switch ($pid) {
case -1:
// fatal error 致命错误 所有进程crash掉
break;
case 0:
// worker context
exit; // 这里exit掉,避免worker继续执行下面的代码而造成一些问题
break;
default:
// master context
pcntl_wait($status); // pcntl_wait会阻塞,例如直到一个子进程exit
// 或者 pcntl_waitpid($pid, $status, WNOHANG); // WNOHANG:即使没有子进程exit,也会立即返回
break;
}
Мы видим, что у мастера есть вызовpcntl_wait
илиpcntl_waitpid
функция, почему? Прежде всего, мы должны упомянуть здесь два понятия, а именно:
- Процесс-сирота: родительский процесс зависает, а дочерний процесс берет на себя процесс инициализации с pid=1 (wait/waitpid), пока собственный жизненный цикл дочернего процесса не закончится, система не вернет ресурсы, а родительский процесс не возьмет на себя связанное восстановление. операции
- Процесс-зомби: дочерний процесс завершается, родительский процесс не получает статус дочернего процесса через wait/waitpid, а дескрипторы, такие как идентификатор процесса, занятый дочерним процессом, все еще существуют, причиняя вред: например, идентификатор процесса ограничен, а невозможность разблокировать идентификатор процесса приводит к возможным проблемам в будущем.
так,pcntl_wait
илиpcntl_waitpid
Цель состоит в том, чтобы предотвратить превращение рабочих процессов в процессы-зомби.
Вдобавок к этому нам также нужно приостановить мастер и воркер, я использую цикл while, а затемusleep(200000)
Предотвращает загрузку процессора на 100%.
Наконец, мы кратко суммируем и опишем процесс этой многопроцессной реализации с помощью следующего рисунка (1-1):
мастер контролирует работника
Вышеприведенное реализует многопроцессорную и многопроцессорную резидентную память, так как же мастер управляет рабочими процессами? Ответ: Многопроцессорная связь. Не так много, чтобы сказать о google/bing, вот несколько способов:
- Именованные трубы: Заинтересованы
- Очередь: личное ощущение и идея использования Redis в качестве очереди сообщений в бизнесе должны быть согласованы
- Общая память: нарушает принцип «не общайтесь через общую память, общайтесь через общение».
- Сигнал: нести меньше информации
- розетка: незнакомая
Поэтому я выбрал путь «именованных каналов». Поток коммуникаций, который я разработал, выглядит примерно следующим образом:
- шаг 1: создайте рабочий конвейер
- шаг 2: мастер записывает сообщение в рабочий конвейер
- шаг 3: рабочий читает сообщение из рабочего конвейера
Затем я разобью его по одному, конечно, я погуглю/погуглю, если мне особо нечего сказать.posix_mkfifo
создавать именованные каналы,fopen
Открытые файлы (каналы существуют как файлы),fread
читать трубу,fclose
Закройте трубу и выдохните, ха-ха, чтобы мы могли легко реализовать наши идеи выше. Теперь давайте поговорим о проблеме, с которой я столкнулся:fopen
Он заблокирован, чтобы бизнес-код не мог выполняться циклически, думать об этом неправильно, обычноfopen
Обычные файлы не имеют блокирующего поведения.В настоящее время поиск FTM не упоминается.fopen
, ctrl+f поиск по странице "заблокировать", ключевой момент:
fopen() will block if the file to be opened is a fifo. This is true whether it's opened in "r" or "w" mode. (See man 7 fifo: this is the correct, default behaviour; although Linux supports non-blocking fopen() of a fifo, PHP doesn't).
Под переводом, вероятно, имеется в виду "при открытии fifo-файла с использованием режима r или w fopen он всегда будет блокироваться; хотя linux поддерживает неблокирующее открытие fifo, но php не поддерживает его.", решения нет, нет поддержки, я чувствую, что должен сдаться, я думаю, что такая сцена не должна быть без поддержки, пойдем и посмотримposix_mkfifo
, результатом был вне себя от радости:
<?php
$fh=fopen($fifo, "r+"); // ensures at least one writer (us) so will be non-blocking
stream_set_blocking($fh, false); // prevent fread / fwrite blocking
?>
The "r+" allows fopen to return immediately regardless of external writer channel.
В заключении используется "r+", и в то же время мы знаем использованиеstream_set_blocking
предотвратить следующийfread
блокировать. Затем мы используем следующий рисунок (1-2), чтобы кратко обобщить и описать способ общения мастер-рабочий.
мастер получает сигнал
Наконец, проблема, которую нам нужно решить, заключается в том, как мастер принимает сигнал от клиента.
master接收信号 -> pcntl_signal注册对应信号的handler方法 -> pcntl_signal_dispatch() 派发信号到handler
Как показано на рисунке (1-3) ниже,
разное
Затем нам нужно только реализовать стратегию master&worker при различных сигналах, таких как перезапуск рабочего. Здесь следует отметить, что когда мастер получает сигнал перезапуска, рабочий процесс не завершается немедленно, а ждет, пока не будет выполнена бизнес-логика рабочего процесса, чтобы выйти. Конкретный способ:
master接收reload信号 -> master把reload信号写worker管道 -> worker读取到reload信号 -> worker添加重启标志位 -> worker执行完业务逻辑后且检测到重启的标志位后exit
моделирование
Разобравшись с нашей реализацией выше, мы приступаем к кодированию. Перед кодом выполняется простое моделирование следующим образом:
Менеджер класса управления процессами
- attributes
+ master: master对象
+ workers: worker进程对象池
+ waitSignalProcessPool: 等待信号的worker池
+ startNum: 启动进程数量
+ userPasswd: linux用户密码
+ pipeDir: 管道存放路径
+ signalSupport: 支持的信号
+ hangupLoopMicrotime: 挂起间隔睡眠时间
- method
+ welcome: 欢迎于
+ configure: 初始化配置
+ fork: forkworker方法
+ execFork: 执行forkworker方法
+ defineSigHandler: 定义信号handler
+ registerSigHandler: 注册信号handler
+ hangup: 挂起主进程
Процесс абстрактного класса Процесс
- attributes
+ type: 进程类型 master/worker
+ pid: 进程ID
+ pipeName: 管道名称
+ pipeMode: 管道模式
+ pipeDir: 管道存放路径
+ pipeNamePrefix: 管道名称前缀
+ pipePath: 管道生成路径
+ readPipeType: 读取管道数据的字节数
+ workerExitFlag: 进程退出标志位
+ signal: 当前接受到的信号
+ hangupLoopMicrotime: 挂起间隔睡眠时间
- method
+ hangup: 挂起进程(抽象方法)
+ pipeMake: 创建管道
+ pipeWrite: 写管道
+ pipeRead: 读管道
+ clearPipe: 清理管道文件
+ stop: 进程exit
основной класс сущности MasterProcess
- attributes
+
- method
+ hangup: 挂起进程
рабочий класс сущности MasterProcess
- attributes
+
- method
+ dispatchSig: 定义worker信号处理方式
Последнее, что нам нужно сделать, это изящно заполнить наш код.
Наконец
адрес проектаGitHub.com/tiger B/ включает…
Недостатков в личных знаниях еще много.Если что-то не так в написании, надеюсь со временем исправите.
THX~