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

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

我很担心,因为我认为这可能会导致服务器崩溃,因为未closures的请求的高负载。 是的,我试过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; } 

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

当HTTP客户端代表该客户端运行FastCGI请求时,Web客户端关闭其传输连接时,Web服务器将中止FastCGI请求。 情况似乎不太可能; 大多数FastCGI请求将具有较短的响应时间,如果客户端速度较慢,Web服务器将提供输出缓冲。 但FastCGI应用程序可能会延迟与另一个系统的通信或执行服务器推送。

不幸的是,它看起来既不是最初的fast-cgi实现,也不是PHP-FPM支持FCGI_ABORT_REQUEST信号,所以不能被中断。

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

  • 将其推送到需要处理的任务队列。
  • 将“任务ID”返回给客户端。
  • 让客户定期进行轮询,看是否完成了“任务”,完成后显示结果。

除了这三个基本的东西 – 如果您担心客户端不再对请求的结果感兴趣时,担心浪费系统资源,您应该添加:

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

你不会说你长时间运行的任务是什么 – 假设它是从另一台服务器上下载一个大的映像文件,操纵那个映像,然后把它存储在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 

所以只请求客户端有兴趣继续处理。

顺便说一句,我强烈建议使用的东西,旨在作为一个队列持有的任务队列(例如Rabbitmq , Redis或Gearman ),而不是仅仅使用MySQL或任何数据库。 基本上,SQL在队列方面并不是很好,你最好从一开始就使用适当的技术,而不是使用错误的技术来启动,然后在数据库变为紧急状态时必须将其交换出去当它试图做数以百计的插入时重载,每秒更新只是为了管理任务。

作为一个副作用,通过将长时间运行的过程分解成任务,变得非常容易:

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

你在长时间的请求中究竟做了什么? 如果你正在做的是导致FastCGI进程等待一些系统调用,比如等待数据库返回一个结果,那么中断的HTTP客户端连接不会导致这个调用被中断。 如果我没有ignore_user_abort(false)ignore_user_abort(false)的效果仅仅是PHP脚本在尝试向(现在丢失的)连接输出内容时立即中止。 脚本在等待系统调用时不会写任何输出。

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

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

在PHP文档中,如果你喜欢,你可以找到关于连接处理的更多信息。