【问题标题】:PHP Parallel processing for a Metasearch Engine元搜索引擎的 PHP 并行处理
【发布时间】:2013-07-14 23:09:38
【问题描述】:

我开发了一个元搜索引擎,我想做的优化之一是并行处理搜索 API。想象一下,搜索引擎 A 在 0.24 秒内检索到结果,SE B 在 0.45 秒内检索到,从 SE C 在 0.5 秒内检索到结果。使用其他开销,元搜索引擎可以在大约 1.5 秒内返回聚合结果,这是可行的。现在我想做的是并行发送这些请求,而不是像目前那样串行发送,并将时间缩短到一秒以下。我调查了 exec、fork、threading 和所有,由于各种原因,都失败了。现在我只花了一两天时间,所以我可能错过了一些东西。理想情况下,我想在我的开发机器(localhost)上的 WAMP 堆栈上实现它,然后看看如何在 Linux 网络服务器上实现。任何帮助表示赞赏。

让我们举个简单的例子:假设我们有两个要同时运行的文件。文件 1:

<?php
// file1.php
echo 'File 1 - Test 1'.PHP_EOL;
$sleep = mt_rand(1, 5);
echo 'Start Time: '.date("g:i:sa").PHP_EOL;
echo 'Sleep Time: '.$sleep.' seconds.'.PHP_EOL;
sleep($sleep);
echo 'Finish Time: '.date("g:i:sa").PHP_EOL;
?>

现在,假设文件二是相同的......想法是,如果并行运行,时间的命令行输出应该是相同的,例如:

File 1 - Test 1
Start Time: 9:30:43am
Sleep Time: 4 seconds.
Finish Time: 9:30:47am

但无论我使用 exec、popen 还是其他什么,我都无法让它在 PHP 中工作!

【问题讨论】:

  • 所有标志都指向exec()
  • 举个简单的例子:假设我们有两个文件要同时运行。 File 1:
  • @ConorRyan 检查更新的答案 - 我提供了一个工作示例。

标签: php parallel-processing meta-search


【解决方案1】:

我会使用socket_select()。这样做,只有连接时间会累积,因为您可以从并行的套接字中读取。这将为您带来巨大的性能提升。

【讨论】:

    【解决方案2】:

    有一种可行的方法。创建一个 cli php 文件,该文件在参数中获取它必须执行的操作并返回序列化生成的任何结果。

    在您的主应用程序中,您可以 popen 任意数量的这些工作人员,然后在一个简单的循环中收集输出:

    [edit]我使用了您的工人示例,只需 chmod +x 并在顶部添加 #!/usr/bin/php 行:

    #!/usr/bin/php
    <?php
    echo 'File 1 - Test 1'.PHP_EOL;
    $sleep = mt_rand(1, 5);
    echo 'Start Time: '.date("g:i:sa").PHP_EOL;
    echo 'Sleep Time: '.$sleep.' seconds.'.PHP_EOL;
    sleep($sleep);
    echo 'Finish Time: '.date("g:i:sa").PHP_EOL;
    ?>
    

    还稍微修改了运行脚本——ex.php:

    #!/usr/bin/php
    <?php
    $pha=array();
    $res=array();
    $pha[1]=popen("./file1.php","r");
    $res[1]='';
    $pha[2]=popen("./file2.php","r");
    $res[2]='';
    while (list($id,$ph)=each($pha)) {
        while (!feof($ph))
            $res[$id].=fread($ph,8192);
        pclose($ph);
    }
    echo $res[1].$res[2];
    

    这是在 cli 中测试的结果(从 web 调用 ex.php 时结果相同,但 file1.php 和 file2.php 的路径应该是固定的):

    $ time ./ex.php 
    File 1 - Test 1
    Start Time: 11:00:33am
    Sleep Time: 3 seconds.
    Finish Time: 11:00:36am
    File 2 - Test 1
    Start Time: 11:00:33am
    Sleep Time: 4 seconds.
    Finish Time: 11:00:37am
    
    real  0m4.062s
    user  0m0.040s
    sys   0m0.036s
    

    从结果中可以看出,一个脚本需要 3 秒才能执行,另一个需要 4 秒。两者并行运行 4 秒。

    [结束编辑]

    这样慢操作会并行运行,你只会串行收集结果。

    最后需要(最慢的worker时间)+(收集时间)来执行。由于收集结果的时间和反序列化的时间等可能会被忽略,因此您将获得最慢请求时间的所有数据。

    作为旁注,您可以尝试使用比内置序列化器快得多的igbinary 序列化器。

    如 cmets 中所述:

    worker.php 在 Web 请求之外执行,您必须通过参数传递其所有状态。传递参数也可能是处理所有转义、安全等的问题,因此使用base64 是无效但简单的方法。

    这种方法的一个主要缺点是不容易调试。

    可以通过使用stream_select而不是fread来进一步改进,同时并行收集数据。

    【讨论】:

    • 工作代码不会在与应用程序相同的上下文中运行。这将使得无法立即调试应用程序,并且需要与工作脚本通信所需的应用程序上下文。还需要注意异常和/或错误处理以使其正常工作(模拟,使用返回码和消息-> 奇怪)。 socket_select()的方法
    • 我想过这个。我现在认为socket_select() 的优势取决于连接到服务器所需的时间。如果需要很长时间,您的解决方案可能会更好。为了保持体育精神,我必须承认这一点。如果您对即用型工作者解决方案感兴趣,请查看Gearman
    • 对不起,如果我的评论令人反感。不要认为这是个人的,这只是因为我喜欢socket_select()|stream_select() 并且认为它们在很多情况下都很好。以你的回答为例。您可以使用stream_select() 并行读取所有工作人员(如果您使用proc_open() 开始该过程)。但是,由于连接时间在我的答案中是累积的,与您的不同,我最终会说您的答案是最好的解决方案。 +1
    • @bbonev 嗯,我无法实现该解决方案...一方面有一个未定义的偏移量...但 gearman 解决方案值得研究...
    猜你喜欢
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2013-09-03
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多