【问题标题】:PHP script continuing to run after page close [initiated from AJAX]页面关闭后 PHP 脚本继续运行 [从 AJAX 启动]
【发布时间】:2016-02-08 19:49:53
【问题描述】:

我在谷歌上告诉我的所有事情都应该不发生,但它确实发生了。

我正在构建一个迁移工具来构建一个“主数据库”。我有一个管理面板,只有少数选定的人可以访问。有一个合并按钮,用于启动 AJAX 调用以运行迁移 php 函数。考虑到我仍在开发它,我不确定这个脚本需要多长时间,但我预计一旦推送到生产环境并填充生产数据库至少需要 20 分钟。我不需要关于最佳实践的讲座告诉我不要通过 GUI 来做。这也将成为一个 cron,但是如果管理员愿意,我希望能够手动诱导它。

这是我的流程。迁移功能立即关闭会话session_write_close() 允许我同时运行多个 php 脚本。我这样做是因为我启动了一个 setInterval 来检查会话变量。这是我的“进度”,它只是我正在进行的循环迭代的一个整数。在我的迁移脚本中,我打开会话,将 1 添加到该 int,然后再次关闭会话。我在每个循环结束时执行此操作。

通过这样做,我成功地为我的 AJAX 创建了一个进度。现在我注意到了一些事情。如果我开始迁移,请关闭我的选项卡 - 或刷新。一旦我重新加载页面,我的进度就会继续增长。这告诉我迁移脚本仍在后台执行。

如果我 100% 关闭浏览器,或清除会话,我将不再看到进度上升。然而,这并不是因为脚本停止了。这是因为我的进度指示依赖于会话,一旦我清除会话或关闭浏览器,我的会话 cookie 就会更改。但是我知道脚本仍在运行,因为我可以手动查询数据库并查看正在添加的条目。

现在回答我的问题: 我不想要这个。如果我的浏览器关闭,如果我按下刷新,如果我失去连接等,我希望脚本被终止。我希望它停止中间进程。

我试过 ignore_user_abort(false);但是我很确定这是特定于命令行的,对我没有任何影响。

我希望终止它,因为我正在构建一个“进度恢复”功能,我们可以在其中选择再次恢复迁移进度的位置。

有什么建议吗?

更新:

我不想走这条路,但我想的一些解决方案是我可以有另一个会话变量。这是我的“最后一次验证客户”,这可能是一个时间戳。在我的 javascript 中,在客户端,每隔 30 秒我就可以点击一个 php 脚本来“更新上次验证客户端时”。例如,在每个循环开始时的迁移函数中,我可以检查以确保时间戳不像 60 秒旧。如果它是 60 秒或更早,我会做一个 die 从而停止我的脚本。这在本地意味着“如果没有客户端更新此时间戳,那么我们可以假设用户关闭了他的浏览器/选项卡/刷新”。至于功能,我可以在命令行(cron)中忽略此检查。不是理想的解决方案,但这是我的计划 B

【问题讨论】:

  • 这实际上是不可能做到的——无论如何也不容易。一旦执行 PHP 脚本,它就会一直运行,直到它停止或被您告知停止。关闭浏览器选项卡并不足以做到这一点。您可以中止 AJAX 调用,但这是不同的,仍然无法实现您希望实现的目标。
  • 将迁移过程分成许多较小的步骤。编写一个管理器并轮询进程并让它接收开始/停止/重启命令。
  • @dgig 事实并非如此,相反。如果客户端断开连接,php 脚本的执行将立即终止,如果 php 在 http 服务器环境中用作模块,这是通常的情况。如果使用另一种方法,例如 cgi 或 fastcgi,那确实是不同的。这就是为什么函数ignore_client_abort() 存在的原因,实际上......
  • 会不会其实不是php脚本还在执行,而是数据库服务器还在处理?

标签: php ajax execute continuous


【解决方案1】:

我现在并且确实采用了从客户端 ping 以指示客户端是否还活着的解决方案。

所以基本上这就是我所做的:

从客户端,在 javascript 中,我设置了一个 setInterval 以每 1.5 秒运行一次,并通过 AJAX 访问一个 php 脚本。此 php 脚本使用当前时间戳更新会话变量(如果您需要,这很容易成为数据库值,但我不想要另一个查询的开销)。

$_SESSION['migration_listsync_clientLastPing_'.$decodedJson['progressKey']] = time();

然后,在我的迁移函数中,我运行检查以查看“时间戳”是否超过 10 秒,如果是,我会死 - 从而杀死脚本。

if(isset($_SESSION['migration_listsync_clientLastPing_'.$progressKey])){
    $calc = time() - $_SESSION['migration_listsync_clientLastPing_'.$progressKey];
    if($calc > 10){
        die();
    }
}

我添加了一个“progressKey”参数,它是一个 1-100 的随机数,在调用函数时生成。这个数字在 javascript 中生成并传递给我的两个 AJAX 调用。这样,如果用户刷新页面然后立即再次按下按钮,我们将不会运行 2 个函数实例。 “旧”实例将在几秒钟后死亡,而新实例将接管。

这不是一个理想的解决方案,但它是一个有效的解决方案。

【讨论】:

    猜你喜欢
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 2014-07-31
    • 2022-01-17
    • 1970-01-01
    相关资源
    最近更新 更多