我有一个使用 exec('command args > /log/file &'); 的 PHP 脚本 在循环中创建多个同时运行的子脚本。基本上,父脚本从数据库中获取用户信息并创建并行运行的子脚本,然后子脚本创建一封电子邮件以发送给单个用户。这种情况大约发生 50,000 次。
为了防止创建 50,000 个同时运行的进程,我有一个数据库表来跟踪当前正在运行的进程,并且在创建新进程之前,父进程会检查当前子进程的数量,如果当前有 25 个子进程处于活动状态,则父进程会休眠。孩子在完成其任务后,会删除其在表中的行,从而释放父母以创建更多的孩子。
问题是,大约 10% 的 exec 命令无声无息地失败,而且似乎没有任何原因。我可以再次运行父脚本(它很聪明,不会向同一个用户发送两次电子邮件),它会再次工作,90% 的时间使用上次失败的相同 exec 命令。连续运行脚本五六次会向所有人发送电子邮件。
通过在执行官之后立即睡觉,我可以将成功率提高到 95% 左右。
如果相同的命令稍后会起作用,为什么 exec 会失败?我可以让脚本重复直到它完成,但我更愿意解决 exec 问题。
一些高度简化的示例代码:
父脚本:
do {
//get user, group, and supergroup information for users that haven't
//been emailed yet
foreach ($users as $userArray) {
$processId = insertIntoProcessQueue($userArray);
$cmd = 'sudo php -q ./childScript.php ' . cliArg($userArray) . ' ' .
cliArg($groupArray) . ' ' . cliArg($supergroupArray)
' ' . $proccessId . ' > file.log &';
exec($cmd);
do {
if (numChildren() >= 25) {
sleep(1);
$waiting = true;
}
} while ($waiting);
}
$incomplete = moreUsersToEmail() > 0 ? true : false;
} while ($incomplete);
function cliArg($array) {
return escapeshellarg(json_encode($arg));
}
子脚本:
ignore_user_abort(true);
$user = json_decode($argv[1]);
$group = json_decode($argv[2]);
$supergroup = json_decode($argv[3]);
print_r($user);
$email = createEmail($user, $group, $supergroup);
$email->sendEmail();
removeFromProcessQueue($argv[4]);
flush();
exit;
print_r 只会在脚本完成时显示在日志文件中,而且我从来没有收到任何错误,所以我无法获得有关它失败原因的任何数据。除此之外,它不会在任何单个用户上始终失败,并且一次运行单个用户也不会失败,因此我必须通过每个人运行脚本并尝试在 45,000 个错误中捕获错误好好工作。而且,由于父母和孩子永远不会在启动孩子的父母之外进行交流,所以我无法(从父母)检测到孩子何时失败(否则我可以立即尝试再次启动任何失败的孩子,而不是事后重新运行父母)。
编辑:所以事实证明,有一个包含的脚本是动态生成的,每次使用时都会被销毁和重新生成(不要问我为什么),这会在并行运行进程时产生竞争条件,导致脚本失败。
感谢大家不幸地浪费了时间。