从服务器进程中实时获取反馈 [使用PHP]

3

需求:

我需要运行一个后台进程(按照用户的请求),这个进程需要大约30至60秒才能完成。 我想给用户一些状态反馈。注意:Toly是对的,“Background”不是必需的。

现状:

这个进程在执行时会打印大约20条状态消息,我通过proc_open并使用fgets监听读取管道来检索它们。我可以将这些消息保存到会话变量中,并使用时间戳(以帮助调试)来查看会话数组随着进程的进行而被写入这些消息。

问题所在:

我的计划是使用ajax调用轮询服务器(每秒一次)来检索这些会话变量,以便在DOM中显示它们。瓶颈似乎是服务器在运行后台进程时无法服务于ajax请求。当后台进程完成时,所有内容都会一起输出。从我所能看到的,问题不在于输出缓冲区,因为每个处理消息都保存有(调试)时间戳,这样就可以看到服务器按顺序写入会话变量,因此我知道proc_open和pipe读取按预期工作。问题似乎是服务器不能在完成进程或循环读取管道之前向AJAX请求提供其JSON对象。

明显的误解:

我认为将进程发送到后台(使用&)可能会给我一个解决方案。显然,我不知道后台进程和分岔进程之间的区别。在此情况下运行进程时,后台模式是否有任何好处?

可能的解决方案:

  1. 我不希望用户启动的此进程/场景很重,但如果这个解决方案中有什么可以帮助处理大量数据的东西,我愿意尝试。
  2. 这是多线程(pthreads)还是多进程(fork)解决方案?
  3. 或者,我应该保存一个进程ID,在while(..fgets..)语句中进行轮询,然后在服务器服务ajax请求后返回到进程?
  4. 我可以运行虚假的状态消息,然后在完成后准确地响应结果。处理请求的时间不取决于用户,因此我的虚假时间可以相当准确。但是,我想知道提供实时反馈的解决方案是什么。
2个回答

3

在谷歌上搜索了一整天,寻找一个可以获取您所描述的相同行为的技术,我想出了一个适用于我的项目的简单解决方案。

一些重要的理论: - session_start() 和像 $_SESSION["x"] = "y" 这样的设置将始终锁定会话文件。

案例场景: - A - process.php - 通过 AJAX 调用运行 - B - get_session.php - 第二个 AJAX 调用;

主要问题是/曾经是,即使您在通过 AJAX 运行的过程中设置了 $_SESSION,它也必须等待会话文件解锁,这会导致两个进程(A.+ B.)之间的同步 - 两者同时完成!

因此,修复此问题并获得良好结果的最简单方法是在每次设置后使用 session_write_close()。例如:

%_SESSION["A"] = "B";
$_SESSION["x"] = "y";
session_write_close();

PS:最佳方法是拥有一组自定义函数来处理会话。

抱歉,因为我刚创建了一个堆栈账户,所以还有标记。


0

你为什么认为你需要一个后台进程?而且,你从哪里得到了你需要一个的想法?

一个正常的php脚本,设置足够的超时时间,并在每个步骤使用flush()函数,将为您提供所需的AJAX输出。

更容易的是,由于您使用会话 - AJAX请求到一个单独的处理程序,它将只检查会话中的内容,如果有新的内容 - 将返回给您新的部分。

$_SESSION['progress'] = array();

在 process.php 内部

$_SESSION['progress'][] = 'Done 5%';
// complete some commands
$_SESSION['progress'][] = 'Done 10%';

在ajax.php内部

if(count($_SESSION['progress']) > $_GET['laststep']) {
    // echo the new messages
}

在您的普通页面内部

$.ajax('ajax.php', 'GET', 'laststep=1', success: function(data){ show(data);})

这样应该可以工作。


你确切地描述了我的方法,但我仍然遇到了列出的问题。如果我有一个while循环,它让我保持在proc_open管道上 - 即使它有一个sleep() - 它似乎直到完成才会处理ajax请求。我一直在倾倒缓冲区(所有缓冲区)。你认为ajax调用是由“单独的处理程序”处理的 - 意味着一个单独的进程,因此我创建一个单独的线程或进程的想法是不需要的? - Ricalsin
拥有单独的处理程序和单独的进程 - 或者是进程内的另一个线程 - 这些都是不同的事情。"如果主进程完成..."这种情况总结了问题所在。在php中,除非采取措施来解决进程/线程问题,否则sleep()是阻塞的,因此您的建议(实际上)不可行 - 它是可行的,但需要更详细的解释(而且在我看来不值得)。感谢您的回复,但它并没有回答我的问题。 - Ricalsin
正如我试图向您解释的那样 - 您不需要sleep()函数。假设您正在执行一些在名为'process.php'的php脚本中耗时的操作 - 在执行这些操作时,您可以定期更新一个$_SESSION变量。客户端的请求应发送到另一个php脚本,让我们称之为'ajax_handler.php'。在'ajax_handler.php'中,您可以访问$_SESSION变量的当前值并将进度返回给浏览器。 - Anatoliy Kim
一个分叉进程建立了一个重复环境。在我的观点中(如果我错了我会很高兴),一个进程在执行完成之前不会写入任何会话变量(供其他进程读取)。或者,通过管道创建连接不像延迟承诺那样工作,当有输出要读取时,您可以回到该连接。 - Ricalsin
这里我向您介绍session_write_close(),它可以从长时间运行的进程中释放会话锁定。您将需要重新打开会话才能再次写入它,但是有代码解决方案:https://dev59.com/m2ct5IYBdhLWcg3wI6J6 - Anatoliy Kim
显示剩余5条评论

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