代码网上找了些、原本只是守护进程、现在是单任务多进程守护、
因为守护进程一般都是后台死循环、所以执行不到下一个任务
代码只能在linux下运行、且依赖 pcntl扩展
启动
[root@localhost wwwroot]# php ./MutiProcesserDeamon.php start
Start , Fork num : 5查看:
[root@localhost ~]# ps -ef | grep muti_processer | grep -v grep
root 3720 1 0 14:32 ? 00:00:00 muti_processer_worker
root 3721 3720 0 14:32 ? 00:00:00 muti_processer_worker
root 3722 3720 0 14:32 ? 00:00:00 muti_processer_worker
root 3723 3720 0 14:32 ? 00:00:00 muti_processer_worker
root 3724 3720 0 14:32 ? 00:00:00 muti_processer_worker
root 3725 3720 0 14:32 ? 00:00:00 muti_processer_worker
[root@localhost wwwroot]# php ./MutiProcesserDeamon.php status
Is Running , Exists process num 5
杀掉:
[root@localhost wwwroot]# php ./MutiProcesserDeamon.php stop
Kill process 3710
Kill process 3711
Kill process 3712
Kill process 3713
Kill process 3714
Kill process 3715
Stoped
代码:
<?php
class MutiProcesserDeamon {
public $pidFile;
public $processNamePrfix ='task_processer';
private $_jobs = null;
public $pidpath= '/tmp';
public $forkNum = 5;
public function __construct($pidFile='') {
if(!$pidFile){
$this->pidFile = $this->pidpath . '/' .__CLASS__ .'_'.date('Ymd'). '_pid.log';
}else{
$this->pidFile = $pidFile;
}
}
private function _demonize() {
if (php_sapi_name() != 'cli') {
die('只可在命令行执行 php ./MutiProcesserDeamon.php start|stop|status');
}
//洗掉原本存在的进程号
try{ if(file_exists($this->pidFile))unlink($this->pidFile);}catch(Exception $e){}
umask(0);
$pid = pcntl_fork();
if (-1 === $pid) {
die('fork fail');
} elseif ($pid > 0) {
//并没有真正的死掉、保存下来,stop的时候一起杀掉
file_put_contents($this->pidFile, $pid.PHP_EOL,FILE_APPEND);
// pcntl_wait($status); //就不会退出、卡在命令行
exit;
}
posix_setsid();
$child = 0;//当前的子进程数量
while (true) {
$child++;
$pid = pcntl_fork();
cli_set_process_title($this->processNamePrfix."_worker");
if ($pid) {
file_put_contents($this->pidFile, $pid.PHP_EOL,FILE_APPEND);
if ($child >= $this->forkNum) {
pcntl_wait($status);
$child--;
}
} else {
$_pid = getmypid();
if (!empty($this->_jobs)) {
$job = $this->_jobs;
if (!empty($job['argv'])) {
call_user_func($job['function'], $job['argv'],$_pid);
} else {
call_user_func($job['function'],$_pid);
}
}
exit(0);
}
}
return;
}
private function _getPid() {
if (!file_exists($this->pidFile)) {
return false;
}
return file($this->pidFile);
}
public function start($procNums=5) {
$pids = $this->_getPid();
if ($pids) {
echo 'Running , Exists process num '.(count($pids)-1).PHP_EOL;
} else {
echo 'Start , Fork num : '.$this->forkNum.PHP_EOL;
$this->_demonize($procNums);
}
}
public function stop() {
$pids = $this->_getPid();
if ($pids) {
foreach($pids as $pid){
$pid = (int)trim($pid);
posix_kill($pid, SIGTERM);
echo 'Kill process ' . $pid.PHP_EOL;
}
unlink($this->pidFile);
echo 'Stoped' . PHP_EOL;
} else {
//防止文件内进程号不准、没杀死
$cmd = "ps aux | grep ".$this->processNamePrfix."| grep -v grep |awk '{print $2}' | xargs kill -9";
exec($cmd);
echo "Not Running" . PHP_EOL;
}
}
public function status() {
$pids = $this->_getPid();
if ($pids) {
echo 'Is Running , Exists process num '.(count($pids)-1).PHP_EOL;
} else {
echo 'Not Running' . PHP_EOL;
}
}
public function setJob($jobs = array()) {
if (!isset($jobs['function']) || empty($jobs['function'])) {
echo "Need function param\n";
}
if (!isset($jobs['argv']) || empty($jobs['argv'])) {
$jobs['argv'] = "";
}
//因为守护是死循环程序、所以一次只能加一个任务
$this->_jobs = $jobs;
}
public function run($argv) {
$param = is_array($argv) && count($argv) == 2 ? $argv[1] : null;
switch ($param) {
case 'start':
$this->start();
break;
case 'stop':
$this->stop();
break;
case 'status':
$this->status();
break;
default:
echo "params: start|stop|status " . PHP_EOL;
break;
}
}
}
$service = new MutiProcesserDeamon();
$service->forkNum = 5;
$service->pidpath = '/tmp';
$service->processNamePrfix = 'fuckqq';
$service->setJob(array(
'function' => 'test',
'argv' => 'param'
));
$service->run($argv);
//这种尤其适合后台接受、或者是读取队列,比如redis出列
function test($param,$pid) {
$i = 0;
while (true) {
$tm = date('Y-m-d H:i:s');
file_put_contents('/home/wwwroot/d.log', "{$i} {$pid} Now is {$tm} ". PHP_EOL,FILE_APPEND);
$i++;
sleep(5);
}
}