Nginx + PHP:在取消请求时停止进程

时间:2013-11-27 19:19:18

标签: php nginx long-polling

我有Nginx 1.4.4和PHP 5.5.6。我正在进行长轮询请求。问题是,如果我取消通过Ajax发送的HTTP请求,请求仍在处理(它们不会停止)。我在文件末尾使用PHP mail()函数对其进行了测试,并且邮件仍在传输,文件没有停止。)

我很担心,因为我认为它可能导致服务器崩溃,因为未关闭的请求负载很高。是的,我尝试了ignore_user_abort(false);,但没有任何变化。我有可能在Nginx中改变一些东西吗?

  location ~ \.php$ {    
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;  
  }

2 个答案:

答案 0 :(得分:11)

坏消息是你几乎肯定无法解决问题,如何解决问题。客户端在收到请求之前关闭连接时发送的FastCGI信号为FCGI_ABORT_REQUEST

  

Web服务器在HTTP客户端关闭时中止FastCGI请求   代表运行FastCGI请求时的传输连接   那个客户。情况似乎不太可能;大多数FastCGI请求   响应时间短,Web服务器提供输出   如果客户端很慢,则缓冲。但FastCGI应用程序可能是   延迟与其他系统通信或执行服务器   推。

不幸的是,原始fast-cgi implementationPHP-FPM似乎都不支持FCGI_ABORT_REQUEST信号,因此无法中断。

好消息是有更好的方法来解决这个问题。基本上你应该永远不会有需要很长时间才能处理的请求。相反,如果请求需要很长时间来处理,您应该:

  • 将其推送到需要处理的任务队列。
  • 将“任务ID”返回给客户端。
  • 让客户定期轮询以查看该“任务”是否已完成以及何时完成显示结果。

除了这3个基本内容之外 - 如果您担心在客户端不再对请求结果感兴趣时浪费系统资源,您应该添加:

  • 如果客户仍在询问结果,则将任务分解为小部分工作,并且只将任务从一个工作“状态”移动到下一个工作状态。

您没有说明您的长期运行任务是什么 - 让我们假装从另一台服务器下载大图像文件,操纵该图像,然后将其存储在S3中。所以这项任务的状态如下:

TASK_STATE_QUEUED
TASK_STATE_DOWNLOADING //Moves to next state when finished download
TASK_STATE_DOWNLOADED
TASK_STATE_PROCESSING  //Moves to next state when processing finished
TASK_STATE_PROCESSED
TASK_STATE_UPLOADING_TO_S3 //Moves to next state when uploaded
TASK_STATE_FINISHED

因此,当客户端发送初始请求时,它会返回一个taskID,然后当它查询该任务的状态时,可以:

  • 服务器报告任务仍在处理

  • 如果它处于以下状态之一,则客户端请求会将其提升到下一个状态。

TASK_STATE_QUEUED => TASK_STATE_DOWNLOADING
TASK_STATE_DOWNLOADED => TASK_STATE_PROCESSING
TASK_STATE_PROCESSED => TASK_STATE_UPLOADING_TO_S3

因此,只有客户感兴趣的请求才会继续处理。

顺便说一下,我强烈建议使用一些专门用于保存任务队列的队列(例如RabbitmqRedisGearman),而不仅仅是使用MySQL或任何数据库。基本上,SQL在组成队列方面并不是那么好,你最好从一开始就使用适当的技术,而不是使用错误的技术来启动,然后在数据库成为紧急情况时不得不在紧急情况下将其交换掉当它试图进行数百次插入时,每秒更新一次只是为了管理任务。

作为附带好处,通过将长时间运行的过程分解为任务,它变得非常容易:

  1. 查看处理时间的用途。
  2. 查看并检测处理时间的波动(例如,如果CPUS达到100%利用率,则图像调整大小将突然花费更长时间。)
  3. 在缓慢的步骤中投入更多资源。
  4. 您可以向客户端提供状态更新消息,这样他们就可以看到任务中的进度,从而提供更好的用户体验,而不仅仅是“无所事事”。

答案 1 :(得分:2)

你在这些长期运行的请求中究竟做了什么?如果您正在做的任何事情导致FastCGI进程等待某个系统调用(如等待数据库返回结果),则中止的HTTP客户端连接将不会导致此调用被中断。如果我没记错的话,ignore_user_abort(false)的效果仅仅是PHP脚本在尝试向(现在丢失)连接输出内容时中止。在等待系统调用时,脚本不会写任何输出。

如果可能,您应该将长时间运行的脚本执行的任务拆分为较小的块,并在处理它们之间检查连接状态。如果连接已终止,请确保脚本终止:

while (!$done_yet) {
    if(connection_status() != CONNECTION_NORMAL) {
        break;
    }
    do_more_work();
}

在PHP文档中,如果您愿意,可以在connection handling找到更多信息。