- 本文地址: https://www.yangdx.com/2019/02/6.html
- 转载请注明出处
我们可以利用PHP的PCNTL模块,写多进程脚本,我整理了一个模板,代码如下:
<?php
if (PHP_SAPI != 'cli') {
exit('请在命令行模式下执行');
}
//脚本永不超时
set_time_limit(0);
/**
* 多进程类封装
*/
class MultiProgress {
/**
* 允许同时运行的子进程数量
* @var int
*/
private $maxChildNum;
/**
* 子进程pid列表
* @var array
*/
private $childList = array();
/**
* 构造函数
* @param int $maxChildNum 允许同时运行的子进程数量
*/
public function __construct($maxChildNum = 5) {
$this->maxChildNum = $maxChildNum;
}
/**
* 执行
*/
public function run() {
//让内核自动回收子进程,防止产生僵尸进程
pcntl_signal(SIGCHLD, SIG_IGN);
//制造一个死循环,让脚本一直跑
while (true) {
$result = $this->doSomething();
//如没有业务被处理,则休眠10秒再继续
if (!$result) {
sleep(10);
}
}
}
/**
* 业务处理逻辑
* @return boolean 是否有业务被处理了
*/
private function doSomething() {
//查询消息待推送列表
$rows = $this->getMsgList();
//标记是否有数据被处理
$flag = $rows ? true : false;
foreach ($rows as $row) {
//在当前进程当前位置产生分支(子进程)
$pid = pcntl_fork();
if ($pid == -1) { //错误处理:创建子进程失败时返回-1
exit("创建子进程失败");
} elseif ($pid) { //父进程会得到子进程的pid号,所以这里是父进程执行的逻辑
$this->childList[$pid] = $pid;
while ($this->checkChildStatus()) {
sleep(1); //如果子进程已满,则休眠1秒再检测
}
} else { //子进程得到的pid为0,所以这里是子进程执行的逻辑
//得到当前子进程的pid
$mypid = getmypid();
$time = date('Y-m-d H:i:s');
$row = trim($row);
$log = "------------------------\n";
$log .= "$mypid $time\n";
$log .= "$mypid 推送消息:$row\n";
$log .= "$mypid 推送完毕\n";
echo $log;
//必须kill掉子进程,不然会成为僵尸进程
posix_kill($mypid, SIGKILL);
}
}
return $flag;
}
/**
* 检测子进程状态,判断是否已达到数量上限
* @return boolean
*/
private function checkChildStatus() {
foreach ($this->childList as $pid) {
$res = pcntl_waitpid($pid, $status, WNOHANG);
if ($res == -1 || $res > 0) { //子进程已结束
unset($this->childList[$pid]);
}
}
if (count($this->childList) == $this->maxChildNum) {
$isMax = true;
} else {
$isMax = false;
}
return $isMax;
}
/**
* 这里只是模拟获取待推送的数据。
* 请根据你的实际需求更改,如查mysql、redis。
* @return array
*/
private function getMsgList() {
$dataFile = __DIR__ . '/a.txt';
if (is_file($dataFile)) {
$arr = file($dataFile); //读取文件的每一行,存入数组
unlink($dataFile); //读完就删除,避免重复读
return $arr;
} else {
return [];
}
}
}
//示例
$obj = new MultiProgress(10);
$obj->run();
?>
当前,这个模块不支持Windows系统,需要Linux系统才支持(即非Unix类系统不支持此模块)。还有就是,在编译PHP的时候要加上 --enable-pcntl 选项它才会被编译。
在Webserver环境下(如Apache、Nginx等),运行此模块会有不可预料的结果,因此,你只能在命令行模式下执行。
快来评论一下吧!
发表评论