【问题标题】:php and ajax: show progress for long scriptphp 和 ajax:显示长脚本的进度
【发布时间】:2014-10-28 02:28:38
【问题描述】:

我的 php 脚本可能需要很长时间(最多 3-5 分钟),所以我想通知用户进展如何。

我阅读了这个question 并决定使用会话来保存有关工作进度的信息。

所以,我在 php 中有以下指令:

public function longScript()
{
       $generatingProgressSession = new Zend_Session_Namespace('generating_progress');
       $generatingProgressSession->unsetAll();

       .... 

       $generatingProgressSession->total = $productsNumber;

       ...

        $processedProducts = 0;
        foreach($models as $model){

             //Do some processing
             $processedProducts++;
             $generatingProgressSession->processed = $processedProducts;

    }

}

我有一个简单的脚本,用于从会话中获取数据(总数和已处理项目的数量),它们以 json 格式返回。

所以,这里是调用长脚本的js代码:

$.ajax({
    url: 'pathToLongScript',
    data: {fileId: fileId, format: 'json'},
    dataType: 'json',
    success: function(data){
        if(data.success){
            if(typeof successCallback == "function")
                successCallback(data);
        }

    }
});

//Start checking progress functionality
var checkingGenerationProgress = setInterval(function(){
$.ajax({
 url: 'pathToCheckingStatusFunction',
 data: {format: 'json'},
 success: function(data){
      console.log("Processed "+data.processed+" items of "+data.total);
      if(data.processed == data.total){
          clearInterval(checkingGenerationProgress);
      }
   }
  });
 }, 10000)

因此,长脚本是通过 ajax 调用的。然后在 10 秒后调用一次检查脚本,在 20 秒后 - 秒后等。

问题是在主长脚本完成之前,对检查脚本的请求都没有完成。那么,这意味着什么?那个长脚本消耗太多资源,服务器无法处理任何其他请求?还是我有一些错误的 ajax 参数?

看图:

-----------UPD

这是一个检查状态的php函数:

public function checkGenerationProgressAction()
{
    $generatingProgressSession = new Zend_Session_Namespace('generating_progress');
    $this->view->total = $generatingProgressSession->total;
    $this->view->processed = $generatingProgressSession->processed;
}

我这里用的是ZF1 ActionContext helper,所以这个函数的结果是json object {'total':'somevalue','processed':'another value'}

【问题讨论】:

  • 我想了解为什么这些检查进度的请求在对长 php 脚本的请求完成之前无法完成。

标签: php jquery ajax


【解决方案1】:

我愿意

exec ('nohup php ...');

文件并将其发送到后台。您可以设置长时间运行的脚本在 DB 中插入单个值以显示其进度的点。现在你可以每隔 10 秒或任何时间检查一次是否添加了新值并通知用户。根据您的环境,甚至可以在用户位于项目中的另一个页面时通知用户。

【讨论】:

    【解决方案2】:

    是的,很长的脚本可能会占用整个服务器,并且在此期间发出的任何其他请求都在等待轮到它们。另外,我建议您不要每 10 秒运行一次检查脚本,无论之前的检查是否完成,而是让检查脚本在完成后自行触发。

    以请求待处理的图像为例,您可以将它们链接起来,而不是同时运行 3 个检查请求,以便在任何时候只运行一个检查请求。

    您可以通过将 setInterval() 函数替换为 setTimeout() 函数并在 AJAX 检查请求完成后重新初始化 setTimeout() 来做到这一点

    【讨论】:

    • 我将我的长脚本更改为只是一个带有 sleep(10) 命令的循环 - 所以它不会消​​耗所有内存和处理器资源,但仍然无法检查状态功能。您知道我应该为此任务设置服务器配置中的任何属性吗?
    • 脚本不太可能将“整个”服务器占用到其他任何东西都不会运行的程度。除了数据库服务器上的大量 SQL 查询锁定表之外,我还没有遇到过这种情况。
    【解决方案3】:

    很可能,由于会话锁定,以下调用未完成。当一个线程打开一个会话文件时,没有其他 PHP 线程可以打开同一个文件,因为它是读/写锁定的,直到前一个线程释放它。

    或者你的服务器浏览器限制并发请求,因此等待这个完成。

    我的解决方案是以某种方式分叉或中断长期运行的脚本。也许调用exec 到另一个具有必要参数的脚本,或者您认为可行的任何方式。将长时间运行的脚本分成一个单独的线程并从当前线程返回,通知用户执行已经开始。

    第二部分是在某处记录脚本的进度。数据库、Memcache 或文件都可以使用。只需在后续调用可以检查的预定位置设置一个值。

    并不是说“预先确定”对每个人都不应该是一样的。它应该是只有用户会话和工作人员知道的位置。

    【讨论】:

    • 我将我的“CheckingStatusFunction”php 代码更改为在不使用会话的情况下返回预定义的值,但它仍然无法正常工作 - 等待长脚本完成。
    • 你知道如何检查浏览器或服务器是否阻止并发请求吗?
    • 如果你设置了会话自动启动,那么线程会自动尝试启动会话,从而阻塞(php.net/manual/en/…)。
    【解决方案4】:

    你能把“pathToCheckingStatusFunction”的PHP贴在这里吗?

    另外,我注意到“pathToCheckingStatusFunction”ajax 函数没有数据类型:“json”。这可能会导致问题。你在任何地方都使用 $_POST['format'] 吗?

    我还建议在第一次检查完成后链接检查。如果您需要这方面的帮助,我可以发布解决方案。

    编辑,添加可能的解决方案:

    我不确定使用 Zend_namespace 是正确的方法。我建议使用 session_start() 和 session_name()。从 $_SESSION 中调用变量。

    示例文件 1:

    session_name('test');
    session_start();
    $_SESSION['percent'] = 0;
    
    ...stuff...
    
    $_SESSION['percent'] = 90;
    

    示例文件 2(获取百分比):

    session_name('test');
    session_start();
    echo $_SESSION['percent'];
    

    【讨论】:

    • 我在 ajax 调用中添加了 'dataType' 属性,但它不起作用。另外,我添加了我的 php 函数来检查状态,所以也许它可以理解问题