swoole 实现php多进程同步
PHP 本身是一个强领域的语言,主要应用于web开发。
PHP 也可以进行多进程开发,但是使用的第三方扩展。
下面我们演示使用 swoole 实现 PHP多进程,且自定义进程名称,可启动及停止。
自定义进程名前缀:tprocess-
主进程名为:tprocess-master
子进程名为:tprocess-xxx xxx 为数字
停止进程有多种方式,比如 kill -9 强制杀死,但这样会导致任务在中间中断,出现脏数据
我们使用信号的方式,通知子进程,在下一个任务时退出。
在使用多进程时,容易出现僵尸进程,为了避免僵尸进程,我们使用 信号接收子进程退消息,并回收资源。
运行
nohup php -f tproc.php start &
停止,会发送信号给运行的主进程,由主进程通知子进程结束
php -f tproc.php stop
启动后效果如下:
[[email protected] test]$ ps -ef | grep tprocess
jingwu 10126 4844 0 00:16 pts/5 00:00:00 tprocess-master
jingwu 10127 10126 0 00:16 pts/5 00:00:00 tprocess-0
jingwu 10128 10126 0 00:16 pts/5 00:00:00 tprocess-1
jingwu 10129 10126 0 00:16 pts/5 00:00:00 tprocess-2
jingwu 10130 10126 0 00:16 pts/5 00:00:00 tprocess-3
jingwu 10131 10126 0 00:16 pts/5 00:00:00 tprocess-4
tproc.php
error_reporting(E_ALL);
ini_set(‘display_errors‘, ‘1‘);
ini_set(‘error_log‘, ‘/tmp/tprocess_error.log‘);
define(‘PROC_LIMIT‘, 5);
class ProcConfig {
static $status = null;
}
//查找对应的进程,我们直接查找系统 /proc 目录下的文件
//这样做是因为,在生产环境不一定会开放 exec 等函数,有一定的局限
//查找系统文件还可以避免维护进程ID文件
function proc_find() {
$baseDir = ‘/proc‘;
$rows = scandir(‘/proc‘);
$procTitles = [];
foreach($rows as $pid) {
if(!is_numeric($pid)) continue;
$cmdlineFile = sprintf("%s/%s/cmdline", $baseDir, $pid);
if(!file_exists($cmdlineFile)) continue;
$title = trim(file_get_contents($cmdlineFile));
if(substr($title, 0, 9) != ‘tprocess-‘) continue;
$procTitles[$title] = $pid;
}
return $procTitles;
}
//检查子进程,因异常或任务结束而导致子进程结束
//通过检查,维持一定数量的子进程
function proc_check() {
$titles = [‘ip‘ => [], ‘domain‘ => []];
for($i = 0; $i < PROC_LIMIT; $i++) $titles[‘ip‘][] = [‘tprocess-‘.$i, $i];
$procs = proc_find();
foreach($titles as $key => $items) {
foreach($items as $row) {
$title = $row[0];
$index = $row[1];
if(isset($procs[$title])) continue;
$worker = new swoole_process(function (swoole_process $process) use($index) {proc_child($process, $index);}, true);
$procid = $worker->start();
}
}
}
//守护进程,负责启动及监控子进程
function proc_master() {
$procs = proc_find();
if(isset($procs[‘tprocess-master‘])) {
print("Dispatch[running]守护进程已经启动\n");
exit(0);
}
//主进程异常结束,子进程未结束,结束所有子进程
if(!isset($procs[‘tprocess-master‘]) && $procs) {
foreach($procs as $title => $pid) posix_kill($pid, 9);
}
//设置主进程名
swoole_set_process_name(‘tprocess-master‘);
}
//worker进程,负责处理数据
function proc_child(swoole_process $process, $index) {
$title = ‘tprocess-‘.$index;
$process->name($title);
while(1) {
if(ProcConfig::$status->get()) exit(1);
sleep(5);
}
}
$action = isset($argv[1]) ? $argv[1] : ‘‘;
if($action == "start") {
//进程状态位,用于标识是否可以退出
ProcConfig::$status = new swoole_atomic(0);
proc_master();
print("started worker\n");
sleep(2);
proc_check();
//注册子进程退出监听函数,避免僵尸进程
swoole_process::signal(SIGCHLD, function($sig) {
while ($ret = swoole_process::wait(false)) {
file_put_contents(‘/tmp/tprocess_defunct.log‘, "child process killed: {$sig}-{$ret[‘pid‘]}\n", FILE_APPEND);
proc_check();
}
});
//主进程监听 SIGUSR2 信号,并设置进程状态位,通知子进程结束
swoole_process::signal(SIGUSR2, function($sig) {
ProcConfig::$status->add(1);
print("receive SIGUSR2 to stop worker\n");
exit(0);
});
}else if($action == "stop") {
$procs = proc_find();
if(isset($procs[‘tprocess-master‘])) {
posix_kill($procs[‘tprocess-master‘], SIGUSR2);
}
print("stop disptch worker is ok\n");
exit(0);
} else {
echo <<< EOF
cmd [action]
action list:
start
stop
eg:
php -f ./tproc.php start
php -f ./tproc.php stop
EOF;
}
原文地址:http://blog.51cto.com/jingwu/2064035
时间: 2024-11-09 17:55:03