【问题标题】:PHP running multiple scripts concurrentlyPHP 同时运行多个脚本
【发布时间】:2017-05-08 23:49:50
【问题描述】:

我有一个这样的对象服务器数组:

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"

但 5 台服务器的整个过程需要 10 秒,而 1 台服务器的时间不到 2 秒。这是为什么?有没有办法减少时间?我的猜测是 exec 命令在进入下一个循环之前等待输出分配给变量?我试着用谷歌搜索了一下,但大多数答案都是为了不返回任何输出......虽然我需要输出

【问题讨论】:

  • start_time=2016-12-23T17:42:50, end_time=2016-12-23T17:43:01。 5 个循环大约 11 秒,第一个循环在 42:51 结束,所以大约 1++秒

标签: php shell foreach exec shell-exec


【解决方案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

干杯!

【讨论】:

    【解决方案2】:

    您可以使用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 中。

    这也可以显示哪个服务器拒绝了连接,以防有关 sshd 的问题影响到您。

    【讨论】:

    • 我意识到它仍然没有同时运行。但它快了一点
    • 这是一种简洁明了的方法。如果你将它分成两个循环,你也许可以加快速度,第一个简单地打开所有使调用在后台运行的句柄(例如$handles[$server]),第二个读取所有响应并关闭把手。就这样我们在期待(并且可能等待)任何东西之前得到所有请求。我想这将足够同时发生!
    • 好的,我到办公室试试,谢谢你的帮助
    • @LimSY 让我知道它的速度有多快。使用microtime() 进行测量。
    • @MarkusAO 这是个好主意。也很容易实现。我将它集成到更新的代码中。
    【解决方案3】:

    是的,您是正确的:您的 PHP 脚本在继续前等待每个响应。

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

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

    【讨论】:

    • 如何实现 cURL 多执行?
    • @LimSY 您将通过 cURL 对服务器进行 SSH 调用,首先使用 PHP 的 ssh_* 函数打开一个隧道,然后启动多 cURL 并将每个请求添加为句柄。 cURL multi init/exec 的基础知识在这里:php.net/manual/en/function.curl-multi-init.phpphp.net/manual/en/function.curl-multi-exec.php ... 以及这里的 SSH 使用示例:stackoverflow.com/questions/22765956/… ... 这种(相当复杂的)并行调用方法只有在你是不过直接连接到远程服务器。
    • 我应该补充一点,我自己没有玩过 cURL / SSH 组合,网上似乎也没有很多信息。如果你走那条路,准备好一点点黑客攻击。 cURL 的另一种选择,可能是更简单的方法,是设置一个基本 API,通过 HTTP 返回所需数据,然后对其进行多次调用。
    • 感谢您的意见,我想我可以像您提到的那样编写一个聚合器脚本
    • 编写一个一次性返回所有值的 bash 脚本应该不会太难。但是,如果这不是由网络延迟引起的问题,您只需将延迟转移到您的 bash 脚本中,除非您在那里分叉或以其他方式异步请求。
    【解决方案4】:

    我不知道如何让你的逻辑更快,但我可以告诉你当我有脚本时我如何使用它来跟踪运行时间。在脚本的开头放一些 var $start = date('c');,最后只是简单的 echo ' start='.$start; echo ' end='.date(c);

    【讨论】:

      猜你喜欢
      • 2017-06-23
      • 2016-11-14
      • 1970-01-01
      • 2013-04-26
      • 1970-01-01
      • 2023-04-07
      • 2021-06-06
      相关资源
      最近更新 更多