为什么KernelEvents :: TERMINATE会阻止对用户的响应?

时间:2015-11-30 15:47:13

标签: php events event-handling silex hhvm

在HVVM上运行的Silex应用程序中,我在内核TERMINATE上设置了一个虚拟事件监听器:

$app['dispatcher']->addListener(
    KernelEvents::TERMINATE,
    function () use ($app) {
        usleep(10000000);
        $app['logger']->alert("I AM REGISTERED!");
    }
);

我希望我的应用程序能够在一秒钟内尽快呈现响应,并且在10秒后我希望消息"I AM REGISTERED"出现在我的日志中。

然而奇怪的是,响应是在事件执行后发送的,这意味着事件会阻止响应10秒,同时我会看到响应和日志消息。

这里发生了什么?

我觉得奇怪的是,在Application.php中,似乎send之前调用了terminate

vendor/silex/silex/src/Silex/Application.php

/**
 * Handles the request and delivers the response.
 *
 * @param Request|null $request Request to process
 */
public function run(Request $request = null)
{
    if (null === $request) {
        $request = Request::createFromGlobals();
    }

    $response = $this->handle($request);
    $response->send();
    $this->terminate($request, $response);
}

2 个答案:

答案 0 :(得分:1)

symfony2 docs about HttpKernel,也就是说,它也是一种,它说:

  

在内部,HttpKernel使用fastcgi_finish_request PHP   功能。这意味着目前只有PHP FPM服务器API   能够在服务器的PHP时向客户端发送响应   进程仍然执行一些任务。使用所有其他服务器API,   kernel.terminate的监听器仍然执行,但响应是   在他们全部完成之前不会发送给客户。

fastcgi_finish_requestnot currently supported by hhvm

因此,除非所有事件都已完成,否则不会发送回复。

答案 1 :(得分:1)

  1. PHP不是异步的,因此虽然可以通过使用回调来处理事件,但只要事件触发,该过程的控制流就会专用于它。

  2. 如果必须进行任何形式的标题修改,框架往往会将内容响应延迟为最后采取的操作。

  3. 正如您所提到的,在TERMINATE事件被触发之前,内容正在被发送/回显,但这不是全部。

    这取决于您的服务器的设置方式。例如,如果你在apache中启用了gzip(很常见),那么 apache 将缓存所有内容,直到PHP完成执行(然后它将gzip并发送它)。你提到你在HHVM上,这也可能是问题 - 它可能不会在执行完成之前刷新内容。

    无论哪种方式,最好的解决方案是......好吧......不要睡觉。我假设你正在睡觉,让数据库有机会刷新到磁盘(10秒是很长时间等待它)。如果情况并非如此,那么找到一个合适的解决方案会很容易,直到我们理解为什么你需要等待那么久。