3

我有一个使用 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 个错误中捕获错误好好工作。而且,由于父母和孩子永远不会在启动孩子的父母之外进行交流,所以我无法(从父母)检测到孩子何时失败(否则我可以立即尝试再次启动任何失败的孩子,而不是事后重新运行父母)。

编辑:所以事实证明,有一个包含的脚本是动态生成的,每次使用时都会被销毁和重新生成(不要问我为什么),这会在并行运行进程时产生竞争条件,导致脚本失败。

感谢大家不幸地浪费了时间。

4

2 回答 2

0

我刚刚查看了 exec() 的 PHP 文档,您可以将数组作为引用传递,第二个参数将填充 exec 的输出。您可以使用它来确定 a) 命令失败的原因和 b) 命令失败的时间并将其集成到您的代码中。

所以我会改变:

exec($cmd);

类似于:

function check_exec_results($results)
{
    echo '<HR><PRE>',print_r($output,true),'</PRE><HR>'; //use this to figure out what output you're getting from the exec commands then remove when you've figured out a way to set $results_look_good below

    $results_look_good = ?; //you will need to edit this yourself to actually do some kind of check

    return $results_look_good;        
}

$successful_exec = false;
do
{
    $exec_results = array();
    exec($cmd,$exec_results);
    $successful_exec = check_exec_results($exec_results);
}
while (!$successful_exec);

请注意,这可能是一个无限循环,因此我还要更进一步,并限制每个用户可以调用 exec() 的次数。

于 2013-08-21T16:17:44.743 回答
0

所以事实证明,有一个包含的脚本是动态生成的,每次使用时都会被销毁和重新生成(不要问我为什么),这会在并行运行进程时产生竞争条件,从而导致脚本失败。

感谢大家不幸地浪费了时间。

于 2016-01-29T18:07:32.777 回答