非阻塞函数PHP

时间:2011-02-10 08:55:29

标签: php

我有一个项目,用户通过表单上传图像,服务器会做一些缩略图。缩略图制作过程非常慢,所以我认为使用非阻塞功能调整图像大小可能是一个很好的解决方案。我的意思是:服务器处理表单(具有更多字段)向用户提供“ok”反馈,然后调用缩略图功能。我怎么能这样做?

提前致谢

7 个答案:

答案 0 :(得分:6)

我通常会采用更好的解决方案:在需要时动态创建缩略图,而不是在上传时。

您可以创建一个动态生成缩略图的脚本,并且所有图像标记都指向此脚本:

<img src="/thumbnail.php?image=foobar.jpg&size=150" />

这会延迟缩略图生成,直到需要它并且异步工作&#34;。使用一些.htaccess重写魔法,您甚至可以使它看起来像普通的图像文件,并以下一次Apache服务器将为其提供服务而不调用脚本的方式缓存图像。


为了更加详细一点,我将其用于用户个人资料图片:

图片代码:

<img src="/img/users/123456/50.jpg" />

htaccess的:

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Rewrites requests for user images to match directory structure.
    # E.g.: URL /img/users/123456/50.jpg -> /img/users/123/123456/50.jpg
    # Intermediate directory level is introduced to avoid cramming too many directories into the same directory.
    RewriteRule ^img/users/(\d{1,3})(\d*)/(\d+\.\D+)$ img/users/$1/$1$2/$3 [nocase,last]

    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

</IfModule>

首先,将图像请求重写为更深层的目录结构。如果图像存在,Apache将照常提供服务。如果不是,则调用我的常规应用程序。在应用中,我将/img/users/...个网址路由到最终包含两条信息的模块:用户ID 123456和请求的大小50。然后它根据这个逻辑粗略地生成一个缩略图:

  1. 查找用户123456
  2. 的个人资料图片
  3. 生成所需尺寸的缩略图
  4. 将缩略图写入/img/users/123/123456/50.jpg,下次将由Apache接收
  5. 输出图像

答案 1 :(得分:6)

您最好的选择是实施Gearman。它是一个作业队列系统,您可以在其中实现异步作业的同步。 http://gearman.org/

答案 2 :(得分:3)

你可以有一个执行缩略图脚本的cronjob。您可以在某种队列(也许是mysql数据库)中添加要调整大小的图像,并且缩略图脚本每分钟运行一次,以检查que中是否有内容然后开始调整大小。

答案 3 :(得分:2)

在一个系统上,我看到了独立后台进程制作缩略图:

  • 表单正常处理,根本不生成任何缩略图
  • 图像被赋予唯一名称并复制到特殊文件夹。
  • thumbnails表中创建数据库条目,链接原始图像和标记为“要缩略图”的新唯一名称
  • 表单处理脚本停止关注并继续处理它需要做的任何事情。

有一个独立的后台进程(及其监视程序),它会持续监视该特殊文件夹(大多数操作系统都有各种工具,可在文件夹内容发生变化时通知您);如果它在那里找到一个图像,它将:

  • 制作缩略图(我们使用的是ImageMagick的CLI)
  • 将其保存在其他地方
  • 更新数据库,将图像状态设置为“缩略图确定”(或“失败”,如果无法制作)

当您需要缩略图时,请检查thumbnails表格 - 如果图像不是“缩略图确定”,则显示占位符,否则获取正确的缩略图名称并显示此内容。

效果很好 - 大多数缩略图都是在几秒钟内创建的,而不会减慢面向用户的脚本速度。请注意,您需要以某种方式启动后台脚本 - 在这种情况下,cron中有一个看门狗,如果它死了则会重新启动缩略图脚本。


@yankee反对某些元素不常见:

  • 缩略图进程没有必要作为后台脚本运行 - 如果你在获得缩略图之前可以忍受一分钟的延迟,你可以将它作为cron脚本运行,完全摆脱看门狗。 / LI>
  • 出于特定的表现原因,选择了ImageMagick而不是GD;缩略图可以使用任何可用的方法。
编辑:我检查了网站,还有一个机制 - 这个没有必要,增加了一些负载,但看起来很酷,特别是如果你不经常看到整页加载(例如在AJAX上)驱动网站):

  • 如果输出“not-failed-but-no-thumbnail”占位符,则缩略图显示在img class="nothumb"
  • JS函数检查具有此类的图像
  • 如果找到,将定期检查缩略图是否可用
  • 静态占位符替换为“加载”占位符
  • 如果找到,它将用缩略图替换占位符

这会在一些资源耗尽后立即加载缩略图。对于连续的后台进程,它并不是真正需要的;但是如果你想确保用户在缩略图可用时看到它们而不是下一个页面加载,这是一个很有用的补充。

答案 4 :(得分:2)

您可以使用http标头告诉客户端在传输“OK”消息后输出已结束,同时保持脚本在服务器上运行以处理缩略图。 我曾经成功使用过这段代码:

header("Connection: close");
@ob_end_clean();
ignore_user_abort();
ob_start();

//generate and print server response here
echo "everything OK";

$size = ob_get_length();
header("Content-Length: ".$size);
ob_end_flush();
flush();

//whatever you do here has no influence on the page loading time, as the client has already closed its connection.
generateThumbnail();

答案 5 :(得分:0)

建议:

  1. 在脚本终止之前关闭您的连接,如下所示:http://www.php.net/manual/en/features.connection-handling.php#71172

  2. 按照此处说明的“实时”生成输出: How to echo output in real time, (before script finishes)? (不会在所有环境中都有效,用户在完成缩略图生成之前仍会获得加载栏)

  3. 启动另一个为您创建缩略图的流程。如果您在服务器上具有相应的权限,请使用system()。如果您没有它们,请在服务器上创建另一个使用URL和套接字调用的PHP脚本。然后,您可以提前终止连接并保持脚本运行以生成缩略图。使用ignore_user_abort()停止缩略图生成,以便在中止使用套接字打开的tcp连接时中止。

答案 6 :(得分:0)

实际上有一种方法, 如果您发回响应标头和响应内容,您实际上可以保持服务器线程继续处理,而不会让客户端继续运行。 因此,你可以发回带有内容类型等的标题..或者标题重定向等。浏览器将读取响应。但这并不意味着服务器线程会停止。

您可以使用连接状态,忽略用户中止并保持服务器中的相同线程 here你可以看到这个解释。

来自提供的链接示例:

<?php
ob_end_clean();
header("Connection: close\r\n");    
header("Content-Encoding: none\r\n");
ignore_user_abort(true); // optional
ob_start();
echo ('Text user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();     // Strange behaviour, will not work
flush();            // Unless both are called !
ob_end_clean();

//do processing here
sleep(5);

echo('Text user will never see');
//do some processing
?>