处理单个阵列的多个“代理”

时间:2011-12-01 14:15:36

标签: php arrays

道歉,如果之前已经涵盖过 - 我进行了搜索,但可能不知道使用的正确术语。

此过程由PHP处理。

情况如下:

我有大量的文件名。我打开的脚本打开这些文件并将其内容输入数据库。一次处理这些文件需要24小时,这些文件每天都会更新。

将单个大型数组拆分为四个较小的数组并运行并发进程在24小时窗口结束之前完成作业,但有时一个或两个进程将在其他进程之前完成数小时,因为文件大小每天都在变化。

就像那些存放零售货架的人(之前曾经做过那个噩梦的人?)在完成自己的任务后帮忙解决剩下的问题,我想在这些“代理人”做的地方有一个脚本同样的。

以下是我所知道的一些基础知识 - 这可能是错的,如果我是,我也不会自豪地抗议: - )

$files = array('file1','file2','file3','file4','file5'); 
//etc... on to over 4k elements

while($file = array_pop($files)){

    //Something in here...  I have no idea what.

}

想法?有一些像四个函数调用或者四个循环的东西已经超出了我的想法,但是我很确定它会等到执行后续调用,直到前一个完成。

感谢任何帮助。我非常认真地坚持这个!

谢谢!

3 个答案:

答案 0 :(得分:2)

你想要的是什么叫做“消息队列”。像beanstalkd

这样的东西

您基本上会创建一个包含各个文件名的邮件列表。然后,您将创建一组处理器来处理它们。每个处理器将处理一个文件,然后返回队列以查看是否有更多消息/文件等待处理。

编辑: 这是一个类比,以帮助解释消息队列。你的第一个想法就像是一个人工经理拿着一堆文件,把它们分成四堆,然后将他的四个员工中的每一个交给他们处理。消息队列更像是这样:管理器将所有文件放在一个表上,并告诉每个员工从表中获取一个文件并对其进行处理。他告诉他们,当他们完成第一个文件以继续获取文件,直到桌面上没有更多文件。完成所有文件后,员工可以回家。

一名员工最终可能会使用非常大的文件而只处理一些文件,而另一名员工可能会获得较小的文件并处理许多文件。每个员工处理的数量无关紧要,他们都会继续工作直到桌子为空。

答案 1 :(得分:2)

数据库支持的消息队列似乎是一个明显的解决方案,但我认为在这种情况下这样做太过分了。我只是将要处理的文件放入一个专用的队列目录中,然后使用DirectoryIterator类进行扫描。像这样:

while (true) {
    look in the queue directory for a file
    if you don't fine one, exit the script, all processing is done
    if you find one, rename it or move it to a work directory
    if the rename/move command succeeded, process the file
    if the rename/move command failed, one of the other threads got it first
}

编辑:

关于启动worker,你可以使用一个简单的shell脚本在后台生成PHP进程:

NUM_WORKERS=5
for WORKER in $(seq 1 ${NUM_WORKERS})
do
    echo "starting worker ${WORKER}"
    php -f /path/to/my/process.php &
done

然后,创建一个cron条目来运行此启动器,例如,在午夜:

0 0 * * * /path/to/launcher.sh

答案 2 :(得分:1)

我会有一个套接字服务器主脚本,它将文件路径分发给x个从属脚本,直到没有剩下的文件要处理。这样,所有从属脚本都将继续运行,您可以根据请求动态分发文件路径。

这样的事情:

<强> master.php

<?php

  // load the array of files to process (however you do this)
  $fileList = file('filelist.txt');

  // Create a listening socket on localhost
  $serverSocket = stream_socket_server('tcp://127.0.0.1:7878');
  $sockets = array($serverSocket);
  $clients = array();

  // Loop while there are still files to process
  while (count($fileList)) {

    // Run a select() call on the existing sockets' read buffers
    // Skip to next iteration if no sockets are waiting for handling
    if (stream_select($read = $sockets, $write = NULL, $except = NULL, 1) < 1) {
      continue;
    }

    // Loop sockets with data to read
    foreach ($read as $socket) {

      if ($socket == $serverSocket) {
        // Accept new clients
        $sockets[] = $clients[] = stream_socket_accept($serverSocket);
      } else if (trim(fgets($socket)) == 'next') {
        // Hand out a new file path to the client
        fwrite($socket, array_shift($fileList)."\n");
        if (!count($fileList)) {
          break 2;
        }
      }

    }

  }

  // When we're done, disconnect the clients
  foreach ($clients as $socket) {
    @fclose($socket);
  }

  // ...and close the listen socket
  @fclose($serverSocket);

<强> slave.php

<?php

  $socket = fsockopen('127.0.0.1', 7878);

  while (!feof($socket)) {

    // Get a new file path from the master
    fwrite($socket,"next\n");
    $path = trim(fgets($socket));

    if (is_file($path)) {
      // Process the file at $path here
    }

  }

然后你需要启动 master.php ,然后当它运行时,你可以根据需要启动很多 slave.php 的实例,他们会所有都继续运行,直到没有更多文件要处理。

显然,这没有错误处理,但它应该提供一个基本框架来帮助您入门。这依赖于阻止函数调用(stream_select()fgets())以避免竞争条件 - 这可能会或可能不足以满足您的目的。