【问题标题】:long poll hanging all other server requests长轮询挂起所有其他服务器请求
【发布时间】:2019-05-01 15:25:10
【问题描述】:

我在我的网站上有一个简单的长轮询请求,以检查是否有任何新通知可供用户使用。就请求而言,一切似乎都完美无缺。仅在更新数据库(并且已为该特定用户创建通知)后才满足请求,然后立即发送新请求。

问题


我注意到的是,当请求正在等待来自数据库的响应时(长轮询应该),对服务器的所有其他请求都会挂起 - 无论是媒体文件,AJAX 请求甚至新页面加载。这意味着对服务器的 所有 请求将挂起,直到我关闭浏览器并重新打开它。

更奇怪的是,如果我访问我的另一个 localhost 站点(我的长期投票是在 MAMP 虚拟主机站点上,www.example.com),它们没问题,我仍然可以使用它们,就好像什么都没有发生了 - 尽管他们在同一台服务器上技术上

我的代码


这是我的客户端 (longpoll.js):

window._Notification = {
    listen: function(){
        /* this is not jQuery's Ajax, if you're going to comment any suggestions, 
        *  please ensure you comment based on regular XMLHttpRequest's and avoid
        *  using any suggestions that use jQuery */
        xhr({
            url: "check_notifs.php",
            dataType: "json",
            success: function(res){
                /* this will log the correct response as soon as the server is 
                *  updated */
                console.log(res);
                _Notification.listen();
            }
        });
    }, init: function(){
        this.listen();
    }
}

/* after page load */
_Notification.init();

这就是我在服务器端所拥有的 (check_notifs.php):

header("Content-type: application/json;charset=utf-8", false);

if(/* user is logged in */){
    $_CHECKED = $user->get("last_checked");

    /* update the last time they checked their notifications */
    $_TIMESTAMP = time();
    $user->update("last_checked", $_TIMESTAMP);

    /* give the server a temporary break */
    sleep(1);

    /* here I am endlessly looping until the conditions are met, sleeping every
    *  iteration to reduce server stress */
    $_PDO = new PDO('...', '...', '...');
    while(true){
        $query = $_PDO->prepare("SELECT COUNT(*) as total FROM table WHERE timestamp > :unix");
        if($query->execute([":unix" => $_CHECKED])){
            if($query->rowCount()){
                /* check if the database has updated and if it has, break out of
                *  the while loop */
                $total = $query->fetchAll(PDO::FETCH_OBJ)[0]->total;
                if($total > 0){
                    echo json_encode(["total" => $total]);
                    break;
                }
                /* if the database hasn't updated, sleep the script for one second,
                *  then check if it has updated again */
                sleep(1);
                continue;
            }
        }
    }
}   

/* for good measure */
exit;

我已经阅读了关于 NodeJS 和其他各种建议用于长轮询的框架,但不幸的是,它们目前对我来说遥不可及,我不得不使用 PHP。我也环顾四周,看看 Apache 配置中是否有任何东西可以解决我的问题,但我只遇到了How do you increase the max number of concurrent connections in Apache?,考虑到我仍然可以使用我的其他 @ 同一服务器上的 987654328@ 网站。

我真的很困惑如何解决这个问题,所以感谢所有帮助,
干杯。

【问题讨论】:

  • 您的问题很可能是if($total > 0){,没有什么可能返回大于零的值,因此循环永远不会结束。
  • 另外,你的查询在每个循环中总是相同的,所以循环在这里似乎没用,因为你总是会返回相同的数据。要更改此设置,请将 $_CHECKED$_TIMESTAMP$user->... 内容移到 while 循环中。
  • @GetOffMyLawn 循环的重点是始终检查数据库是否正在更新:) 但这不是问题(长轮询本身工作正常),问题是它会挂起所有其他请求到那个 [virtualhost?] 服务器
  • 请顶一下@Community
  • 请将$_PDO = new PDO('...', '...', '...');移到while循环之外,看看问题是否仍然存在。

标签: javascript php apache


【解决方案1】:

实际发生的情况是 php 正在等待此脚本结束(锁定)以提供对同一文件的下一个请求。

As you can read here:

  • 某处有一些锁 - 例如,如果两个请求来自同一个客户端,并且您正在使用 PHP 中基于文件的会话:在执行脚本时, 会话被“锁定”,这意味着服务器/客户端将不得不等待 直到第一个请求完成(并且文件解锁)才能能够 使用该文件为第二个用户打开会话。
  • 请求来自同一个客户端和同一个浏览器;在这种情况下,大多数浏览器都会将请求排队,即使有 没有任何服务器端产生这种行为。
  • 当前活动的进程不止 MaxClients —— 请参阅之前 Apache 手册中的引述。

实际上某处有某种锁。您需要检查发生了什么锁。也许$_PDO 有锁,您必须在sleep(1) 之前将其关闭以使其保持解锁状态,直到您发出下一个请求。

您可以尝试提高您的MaxClients 和/或申请this answer

执行session_write_close()(或 cakephp 中的相应函数)在 ajax 端点的开头关闭会话。

【讨论】:

  • session_write_close函数应该放在哪里?
  • @GROVER。在文件的开头。但请记住,这是因为在这种情况下它是锁。在你的情况下,你应该寻找你的锁。你在使用会话吗?
  • 我正在使用会话 :) 但仅适用于某些用户数据
  • @GROVER。你的$user 可能有会话锁定。尝试在最后一个$user->update 或任何需要打开会话的东西之后解锁。
  • 恭喜获得 8K 声誉。 ;)
猜你喜欢
  • 2017-07-05
  • 2010-12-29
  • 1970-01-01
  • 2012-09-28
  • 2014-12-05
  • 2012-08-01
  • 2013-05-25
  • 2011-06-26
  • 1970-01-01
相关资源
最近更新 更多