5

当我遇到一个导致我的 PHP 程序无限循环的错误时,我想到了这个问题。这是一个示例情况:

假设我有一个接收图片上传的 PHP 网页(该页面可能是图像上传表单的响应页面)。在服务器中,脚本应该将图像存储在一个临时文件中。然后脚本应该向客户端输出一条确认消息,然后停止发送数据,这样客户端就不会等待。然后脚本应继续执行,在结束之前处理图像(如调整大小)。

我认为这种“技术”可能很有用,这样客户端就不会在耗时的过程中等待,从而防止超时。

另外,这可以使用 HTTP 方法解决吗?

4

5 回答 5

12

是的。

如果您正确使用 HTTP 标头,则无需任何异步处理即可轻松完成此操作。

在正常情况下,一旦另一端的客户端关闭连接,PHP 就会停止处理。如果你想在这个事件之后继续处理,你需要做一件事:告诉 PHP 忽略用户中止。如何?

ignore_user_abort()

这将允许您的脚本在客户端摆脱闪避之后继续运行。但是我们也面临着如何告诉客户端他们发出的请求已经完成以便它关闭连接的问题。通常,如果我们不指定它们,PHP 会透明地为我们发送这些标头。但是,在这里,我们需要明确地执行此操作,否则客户端将不知道我们何时希望他们停止读取响应。

为此,我们必须发送适当的 HTTP 标头来告诉客户端何时关闭:

Connection: close
Content-Length: 42

这种标头组合告诉客户端,一旦它读取 42 个字节的实体主体响应,消息就完成了,他们应该关闭连接。这种方法有几个后果:

  1. 您必须在发送任何输出之前生成响应,因为您必须确定其内容长度大小(以字节为单位),以便发送正确的标头。
  2. 在回显任何输出之前,您必须实际发送这些标头。

所以你的脚本可能看起来像这样:

<?php

ignore_user_abort();

// do work to determine the response you want to send ($responseBody)
$contentLength = strlen($responseBody);

header('Connection: close');
header("Content-Length: $contentLength");
flush();

echo $responseBody;

// --- client will now disconnect and you can continue processing here ---

这种方法的最大“Gotchya”是当您在 Web SAPI 中运行 PHP 时,如果您在最终用户客户端关闭连接后进行耗时的处理,您可以轻松地违反最大时间限制指令。如果这是一个问题,您可能需要考虑使用 cron 的异步处理选项,因为 PHP 在 CLI 环境中运行时没有时间限制。或者,您可以使用set_time_limitdocs来提高 Web 环境中脚本的时间限制。

值得一提的是,如果您执行此类操作,您可能还希望在生成响应正文时向connection_aborted()文档添加检查,以便在用户在完成传输之前中止时避免额外的处理。

于 2012-08-25T16:06:54.113 回答
2

当我通过 php 的网络服务从 iphone 上传 twitter 和 facebook 上的图像时,我遇到了同样的问题。

如果图像上传的处理时间不多,那么您可以查看此评论@Musa可能对您有所帮助,但如果处理时间过长,请尝试此步骤。

 1. Image store in folder
 2. Fetch image from folder using cron 
 3. Cron run for every 2 min in backend

这些将减少您的处理时间。

希望这对您有所帮助。

于 2012-08-24T06:57:47.760 回答
1

建议异步执行这些操作。也就是说,制作另一个只处理先前创建的 tmp 文件的脚本,并使用 cron 运行它(甚至不涉及 apache)。当 php 作为 web-server 模块运行时,它应该致力于快速形成响应,然后离开为下一个请求释放资源。

你这样想是对的;只需继续在架构上迈出一小步,将请求与需要进行的繁重工作完全分离。

于 2012-08-24T06:41:23.993 回答
1

你可以通过几种方式做到这一点#

1#

ob_start();
//output
header("Content-Length: ".ob_get_length());
header("Connection: close");
ob_end_flush();
//do other stuff

2#

使用 PHP 的 system() 或 exec(),关闭进程

3#

使用 Shell 脚本关闭进程

于 2012-08-24T06:47:00.793 回答
1

您可以使用ob_implicit_flush(),它将打开或关闭隐式刷新。隐式刷新将在每次输出调用后导致刷新操作,因此不再需要对 flush() 的显式调用。

参考

我如何使用 PHP 实现这个场景?

或者

您应该创建一个独立的 cron,它将在特定时间后运行,并以异步方式执行,不让用户知道正在进行的处理,或者不让用户等待。这样,您甚至还可以检测到失败的案例。

你还应该尽量减少加载时间。

于 2012-08-24T06:51:54.630 回答