我有一个需要处理的数据列表。它现在的工作方式是:
这需要太长时间。我想要的是:
这样的事情可能吗?
我在想我每秒都使用CRON来启动脚本实例。所有需要处理的项目都将在MySQL数据库中进行标记,因此每当通过CRON启动实例时,它只会标记要标记的下一个项目并删除该标记。
思想?
编辑:为了澄清一些事情,每个“项目”都作为单独的行存储在mysql数据库表中。每当处理项开始处理时,它都被标记为在db中处理,因此每个新实例将只抓取未处理的下一行并处理它。因此,我不必将这些项目作为命令行参数提供。
答案 0 :(得分:6)
这是一个解决方案,不是最好的解决方案,但在Linux上运行良好:
将处理PHP拆分为单独的CLI脚本,其中包括:
您的主脚本(可能在您的网络服务器上)可以:
后台nohup
启动的应用程序将独立于启动它的脚本运行。
这让我很感兴趣,我决定写一个POC。
test.php的
<?php
$dir = realpath(dirname(__FILE__));
$start = time();
// Time in seconds after which we give up and kill everything
$timeout = 25;
// The unique identifier for the request
$id = uniqid();
// Our "items" which would be supplied by the user
$items = array("foo", "bar", "0xdeadbeef");
// We exec a nohup command that is backgrounded which returns immediately
foreach ($items as $item) {
exec("nohup php proc.php $id $item > $dir/proc.$id.$item.out &");
}
echo "<pre>";
// Run until timeout or all processing has finished
while(time() - $start < $timeout)
{
echo (time() - $start), " seconds\n";
clearstatcache(); // Required since PHP will cache for file_exists
$running = array();
foreach($items as $item)
{
// If the pid file still exists the process is still running
if (file_exists("$dir/proc.$id.$item.pid")) {
$running[] = $item;
}
}
if (empty($running)) break;
echo implode($running, ','), " running\n";
flush();
sleep(1);
}
// Clean up if we timeout out
if (!empty($running)) {
clearstatcache();
foreach ($items as $item) {
// Kill process of anything still running (i.e. that has a pid file)
if(file_exists("$dir/proc.$id.$item.pid")
&& $pid = file_get_contents("$dir/proc.$id.$item.pid")) {
posix_kill($pid, 9);
unlink("$dir/proc.$id.$item.pid");
// Would want to log this in the real world
echo "Failed to process: ", $item, " pid ", $pid, "\n";
}
// delete the useless data
unlink("$dir/proc.$id.$item.out");
}
} else {
echo "Successfully processed all items in ", time() - $start, " seconds.\n";
foreach ($items as $item) {
// Grab the processed data and delete the file
echo(file_get_contents("$dir/proc.$id.$item.out"));
unlink("$dir/proc.$id.$item.out");
}
}
echo "</pre>";
?>
proc.php
<?php
$dir = realpath(dirname(__FILE__));
$id = $argv[1];
$item = $argv[2];
// Write out our pid file
file_put_contents("$dir/proc.$id.$item.pid", posix_getpid());
for($i=0;$i<80;++$i)
{
echo $item,':', $i, "\n";
usleep(250000);
}
// Remove our pid file to say we're done processing
unlink("proc.$id.$item.pid");
?>
将test.php和proc.php放在服务器的同一文件夹中,加载test.php并享受。
你当然需要nohup(unix)和PHP cli来实现这个目的。
很多乐趣,我可能会在以后找到它。
答案 1 :(得分:5)
使用像Beanstalkd这样的外部工作队列,您的PHP脚本也会编写一堆作业。您有尽可能多的工作进程从beanstalkd中提取作业并尽快处理它们。你可以像拥有内存/ CPU那样增加工作量。您的工作机构应该包含尽可能少的信息,可能只是您点击数据库的一些ID。 beanstalkd有很多客户端API,它本身有一个非常基本的API,想想memcached。
我们使用beanstalkd处理我们所有的后台工作,我喜欢它。易于使用,速度非常快。
答案 2 :(得分:1)
答案 3 :(得分:1)
你可以在客户端的javascript中实现线程吗?在我看来,我已经看到了一个实现它的javascript库(来自谷歌?)。谷歌它,我相信你会找到一些东西。我从来没有做过,但我知道它可能。无论如何,你的客户端javascript可以为单独的线程中的每个项激活(ajax)一个php脚本。这可能比尝试在服务器端完成所有操作更容易。
-don
答案 4 :(得分:0)
如果您正在运行高流量的PHP服务器,那么如果您不使用备用PHP缓存,则 INSANE :http://php.net/manual/en/book.apc.php。您不必进行代码修改即可运行APC。
另一个可以与APC一起使用的有用技术是使用Smarty模板系统,它允许您缓存输出,以便不必重建页面。
答案 5 :(得分:0)
为了解决这个问题,我使用了两种不同的产品; Gearman和RabbitMQ。
将您的工作放入某些排队软件(如Gearman或Rabbit)的好处是,您拥有多台计算机,他们都可以参与处理队列中的项目。
Gearman更容易设置,所以我建议先稍微探讨一下。如果你发现你需要更重要的东西和队列稳健性;看看RabbitMQ
答案 6 :(得分:-1)
你可以使用pcntl_fork()和family来分叉一个进程 - 但是你可能需要像IPC这样的东西来回传给子进程(你分叉的那个)完成的父进程。
您可以让他们写入共享内存,例如通过内存缓存或数据库。
您还可以让子进程将已完成的数据写入文件,父进程一直在检查 - 当每个子进程完成时,创建/写入/更新文件,父进程可以抓取它,一个在时间,他们把它们扔回被叫/客户端。
父母的工作是控制队列,确保相同的数据不会被处理两次,并且还要检查孩子们的状态(更好地杀死那个失控的过程并重新开始等等)
要记住的其他事项 - 在Windows平台上你将受到严重限制 - 我甚至认为你无法访问pcntl_,除非你编译PHP并支持它。
此外,您可以在处理完数据后对其进行缓存,还是每次都是唯一数据?这肯定会加快速度..?