Nginx+PHP:在取消请求时停止进程会怎么样?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (201)

我有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;  
  }
提问于
用户回答回答于

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

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

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

在PHP文档中,可以找到有关连接处理的更多信息。

用户回答回答于

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

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

不幸的是,它看起来既不是原始的fast-cgi实现,也不是PHP-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. 可以向客户端发送状态更新消息,以便他们可以看到任务中的进度,从而提供更好的用户体验,而不仅仅是坐在那里“无所事事”。

扫码关注云+社区

领取腾讯云代金券