通过 AJAX 调用在服务器上终止 PHP 脚本

4
我编写了一个PHP脚本来导入大量数据。导入过程是通过Ajax调用触发的,ajax请求一直等待服务器响应。由于我正在使用专用服务器,因此没有超时问题。
问题在于,我们需要一项功能,即可以终止导入进程。例如客户端上的停止按钮。我们认为,如果我们终止等待的ajax调用,那么服务器上的进程也将停止,因为没有请求需要服务。但不幸的是,这并不是事实,脚本在服务器端继续执行,而Ajax请求已经从客户端中断。
其次,我们在该项目中使用PHP会话。假设取消按钮需要在服务器上通过另一个脚本进行Ajax调用以停止进程,那么如果已经有一个等待的ajax请求,该请求如何到达服务器。Php / Apache将保留第二个请求,直到第一个请求周期完成。
注意:根据我们的项目架构,我们需要在每个页面上使用session_start()。如果有人能指导这些问题,那就太好了。

你没有提出问题。 - hobbs
@hobbs 请阅读最后一行:“如果有人能在这些问题上给予指导,那将是很好的。” - asim-ishaq
3
第二点:PHP在脚本积极使用会锁定会话文件。除非您使用session_write_close()来释放锁定,否则只能有一个正在使用会话的PHP脚本在执行中。 - Marc B
6个回答

1
你可以在运行脚本时将进程ID写入文件、数据库或缓存中。你可以使用http://php.net/manual/en/function.getmypid.php获取进程ID。这假定每个脚本都有自己的进程ID。
不使用锁定会话的kill脚本可以读取该进程ID并尝试终止它。

当请求到达服务器时,它只创建一个进程,你确定吗? - asim-ishaq
@asim-ishaq,我不明白你的问题。但正如我所说,这假设每个脚本都有自己的进程ID。如果我没有弄错的话,您还可以配置Web服务器以在同一进程中处理所有请求。但我认为这不是最常用的选项。我经常遇到的情况是Web服务器在其自己的进程中运行(分叉)每个请求。 - nl-x
1
实际上我正在测试另一个想法。还没有成功。这个想法是:用户取消了ajax请求(已完成)。服务器上的PHP脚本将继续检查连接是否被中止,然后终止进程。我使用php函数connection_aborted()来确定连接的状态。如果连接被终止,则它将返回1,否则返回0。问题是它总是返回0,无论请求是否被终止。你有什么想法吗?请参见http://www.php.net/manual/en/function.connection-aborted.php - asim-ishaq
@asim-ishaq 这是个好主意。请参考这个问题/答案了解更多关于连接状态的背景信息。 - grossvogel

1

在PHP中进行长时间运行的过程时要小心,因为PHP的zend引擎和GC不适合长时间运行的过程。

因此,我强烈建议使用适当的作业管理器,如gearman。 gearman确实有php扩展。使用作业管理器将使您完全控制每个进程。您可以启动/停止进程和任务。

另一个选择是使用队列,例如amqp,以更清晰地处理这些任务。哪个更适合您的用例,我会让您决定。


0

你可以将 PHP 脚本的时间限制设置为略高于 AJAX 超时时间,这样如果脚本运行超时就会被终止。类似于以下代码:

<?php

    set_time_limit(20);

    while ($i<=10)
    {
        echo "i=$i ";
        sleep(100);
        $i++;
    }

?>

来源: http://php.net/manual/zh/function.set-time-limit.php


我们使用VPS服务器是为了PHP脚本没有超时限制,因此请为最终用户提供一个终止他在服务器上启动的进程的功能。 - asim-ishaq

0

一旦脚本运行,只有通过结束正在运行脚本的php进程才能停止它。一种可能的方法是在调用另一个脚本时使用会话来存储“继续”条件。

例如:

脚本1是工作程序(导入器)
脚本2是由ajax重复调用的函数,只要导入器工作就会调用。
脚本1和2共享,假设为$_SESSION['lastPing']

所以

脚本2在每次调用时设置$_SESSION['lastPing'] = time();
脚本1有一个条件if($_SESSION['lastPing'] - 30 > time()){ die(); }


由于应用程序使用了PHP SESSION并且第一个请求正在处理中,所以在第一个周期完成之前无法处理第二个请求。因此,如果我们进行另一个ajax调用,它将等待先前的请求完成。这是使用PHP会话脚本的限制。 - asim-ishaq

0

您可以使用proc_函数来处理此问题。以下是可能的步骤概述:

  1. 创建一个唯一的令牌,并将其与开始导入的订单一起传递给服务器。
  2. 当接收到导入订单时(通过AJAX):
    a)存储一个记录,显示该令牌正在处理中(通过文件、数据库、memcached)。
    b)使用proc_open运行导入脚本。
    c)开始轮询循环,检查记录(2a)以查看该令牌是否仍处于处理状态。如果不是,则调用proc_terminate并退出循环。
  3. 如果给出停止导入的订单(在单独的AJAX调用中),则更新持久化记录以指示应停止特定令牌,这将在(2c)中被捕获。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接