PHP同时运行多个脚本的方式

9

我有一个包含服务器对象的数组,看起来像这样:

Array
(
    [0](
                (
                    [id] => 1
                    [version] => 1
                    [server_addr] => 192.168.5.210
                    [server_name] => server1
                )
        )
    [1](
                (
                    [id] => 2
                    [server_addr] => 192.168.5.211
                    [server_name] => server2
                )
        )
)

通过运行以下代码,我可以获得所需的输出。
foreach ($model as $server) {
        $cpu_usage = shell_exec('sudo path/to/total_cpu_usage.sh '.$server->server_addr);
        $memory_usage = shell_exec('sudo path/to/total_memory_usage.sh '.$server->server_addr);
        $disk_space = shell_exec('sudo path/to/disk_space.sh '.$server->server_addr);
        $inode_space = shell_exec('sudo path/to/inode_space.sh '.$server->server_addr);
        $network = shell_exec('sudo path/to/network.sh '.$server->server_addr);
        exec('sudo path/to/process.sh '.$server->server_addr, $processString);
        $processArray = array();
        foreach ($processString as $i) {
          $row = explode(" ", preg_replace('/\s+/', ' ', $i));
          array_push($processArray,$row);
        }
        $datetime = shell_exec('sudo path/to/datetime.sh '.$server->server_addr);
        echo $cpu_usage;
        echo $mem_usage;
        echo $disk_space;
        ......
}

我的脚本类似于:

#!/bin/bash
if [ "$1" == "" ]
then
        echo "To start monitor, please provide the server ip:"
        read IP
else
        IP=$1
fi

ssh root@$IP "date"

但是对比于1台服务器不到2秒的时间,5台服务器的整个过程花费了大约10秒钟。这是为什么呢?有没有办法缩短时间?我猜测exec命令在进入下一个循环之前,正在等待输出被分配给变量?我试图在谷歌上搜索一些答案,但大多数答案都是没有返回任何输出...可是我需要结果。


开始时间=2016-12-23T17:42:50,结束时间=2016-12-23T17:43:01。5个循环大约需要11秒,第一个循环在42:51结束,所以大约需要1++秒。 - Lim SY
4个回答

11

你可以使用popen()同时运行多个脚本,并稍后使用fread()获取输出。

//execute
foreach ($model as $server) {
    $server->handles = [
        popen('sudo path/to/total_cpu_usage.sh '.$server->server_addr, 'r'),
        popen('sudo path/to/total_memory_usage.sh '.$server->server_addr, 'r'),
        popen('sudo path/to/disk_space.sh '.$server->server_addr, 'r'),
        popen('sudo path/to/inode_space.sh '.$server->server_addr, 'r'),
        popen('sudo path/to/network.sh '.$server->server_addr, 'r'),
    ];
}

//grab and store the output, then close the handles
foreach ($model as $server) {
    $server->cpu_usage = fread($server->handles[0], 4096);
    $server->mem_usage = fread($server->handles[1], 4096);
    $server->disk_space = fread($server->handles[2], 4096);
    $server->inode_space = fread($server->handles[3], 4096);
    $server->network = fread($server->handles[4], 4096);

    foreach($server->handles as $h) pclose($h);
}

//print everything
print_r($model);

我测试了一段类似的代码来执行5个沉睡2秒的脚本,整个过程只花费了2.12秒,而不是使用shell_exec()花费的10.49秒。

更新1:非常感谢Markus AO指出了一个优化潜力。

更新2:修改了代码以消除覆盖的可能性。结果现在在$model中。

这也可以显示哪个服务器拒绝了连接,在ssh故障问题影响您时很有用。


好的,我到办公室后会尝试一下,谢谢你的帮助。 - Lim SY
@LimSY 请告诉我速度有多快。使用 microtime() 进行测量。 - Rei
@MarkusAO 那是个好主意。而且很容易实现。我已经在更新的代码中集成了它。 - Rei
确实更快,但出于某种原因,来自不同服务器的一些数据丢失了? - Lim SY
我尝试在每个循环中使用动态变量,并在关闭文件之前进行延迟,但问题仍然存在。你有什么想法吗? - Lim SY
显示剩余7条评论

1
你只需要在Linux命令结尾添加> /dev/null &即可,这样虽然不会有输出但会作为后台(异步)进程运行。
shell_exec('sudo path/to/datetime.sh '.$server->server_addr.'  > /dev/null &');

请参考我在GitHub上的后台进程脚本(它支持Windows兼容的后台进程)。

https://github.com/ArtisticPhoenix/MISC/blob/master/BgProcess.php

干杯!


0

是的,你说得对:你的PHP脚本在等待每个响应后才会继续执行。

我猜你希望同时运行所有服务器的请求,而不是等待每个服务器的响应。在这种情况下,假设你正在运行一个线程安全版本的PHP,请查看pthreads。一种选择是使用cURL multi-exec进行异步请求。然后还有pcntl_fork可能会对你有所帮助。此外,还可以参考thisthis线程中可能的线程/异步方法。

此外,单独测试和基准测试shell脚本以查看瓶颈在哪里,以及是否可以加速它们。这可能比在PHP中设置线程/异步更容易。如果您遇到网络延迟问题,请编写一个聚合器shell脚本,执行其他脚本并在一个请求中返回结果,并仅在您的PHP脚本中调用该脚本。

如何实现cURL多路复用? - Lim SY
@LimSY 你可以通过cURL进行SSH调用到你的服务器,首先使用PHP的ssh_*函数打开一个隧道,然后初始化multi-cURL并将每个请求添加为一个句柄。关于cURL multi init/exec的基础知识在这里:http://php.net/manual/en/function.curl-multi-init.php和http://php.net/manual/en/function.curl-multi-exec.php... SSH使用示例在这里:http://stackoverflow.com/questions/22765956/php-ssh2-tunnel-using-proxy-socks-throw-ssh-server... 这种(相当复杂)的并行调用方法只有在直接连接到远程服务器时才有意义。 - Markus AO
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Markus AO
谢谢您的建议,我想我会写一个聚合脚本,就像您提到的那样。 - Lim SY
如果这不是由于网络延迟引起的问题,那么你只会将延迟转移到你的bash脚本中。为什么呢?难道不是因为PHP的shell_exec函数正在等待输出,因此无法继续向前吗?通过在脚本中完成所有操作,我只需要运行一次shell_exec。 - Lim SY
显示剩余3条评论

0
我不知道如何让你的逻辑更快,但我可以告诉你我在运行脚本时如何跟踪时间。在脚本开始处设置一些变量$start = date('c');,在结尾处简单地使用echo ' start='.$start; echo ' end='.date(c);即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接