【问题标题】:Ajax long polling (comet) + php on lighttpd v1.4.22 multiple instances problemAjax long polling (comet) + php on lighttpd v1.4.22 多实例问题
【发布时间】:2009-11-23 20:43:10
【问题描述】:

我是这个网站的新手,所以我真的希望我能提供有关我的问题的所有必要信息。

我一直在尝试使用长轮询创建“新消息到达通知”。目前我正在通过我网站中每个页面的window.onLoad 事件发起轮询请求。

在服务器端我有一个无限循环:

while(1){
 if(NewMessageArrived($current_user))break;
 sleep(10);
}
echo $newMessageCount;

在客户端,我有以下(简化的)ajax 函数:

poll_new_messages(){
 xmlhttp=GetXmlHttpObject();
 //...
 xmlhttp.onreadystatechange=got_new_message_count;
 //...
 xmlhttp.send();
}

got_new_message_count(){
 if (xmlhttp.readyState==4){
  updateMessageCount(xmlhttp.responseText);
  //...
  poll_new_messages();
 }
}

问题是每次页面加载时,上述循环都会重新开始。结果是多个无限循环每个用户最终使我的服务器挂起。

*NewMessageArived() 函数在 MySQL DB 中查询新的未读消息。

*在php脚本的开头我运行start_session()以获得$current_user的值。

我目前是该站点的唯一用户,因此我可以通过将time() 写入此循环内的文件来轻松调试此行为。我看到的是该文件在 10 秒内被写入的频率超过一次,但它仅在我从一个页面转到另一个页面时才开始。

如果有任何其他信息可能有帮助,请告诉我。

谢谢。

【问题讨论】:

    标签: php javascript ajax comet long-polling


    【解决方案1】:

    我想我找到了解决问题的方法。如果有人能说出这是否是 COMET 中使用的技术以及该解决方案的可扩展性,我将不胜感激。

    我使用了这样一个基于用户的信号量:

    $sem_id = sem_get($current_user);
    sem_acquire($sem_id);
    while(1){
     if(NewMessageArrived($current_user))break;
     sleep(10);
    }
    sem_release($sem_id);
    echo $newMessageCount;
    

    【讨论】:

      【解决方案2】:

      长轮询请求在 30 秒后超时似乎很常见。因此,在您的 while 循环中,您可以在 30 秒后回显“CLOSE”。

      while(!$new_message && $timer < 30){
          $new_message = NewMessageArrived($current_user);
          if(!$new_message) {
              sleep(10);
              $timer += 10;
          }
      }
      if($newMessageCount) {
          echo $newMessageCount;
      } else {
          echo 'CLOSE';
      }
      

      在 Javascript 中,您可以监听 CLOSE。

      function poll_new_messages(){
          xmlhttp=GetXmlHttpObject();
          //...
          xmlhttp.onreadystatechange=got_new_message_count;
          //...
          xmlhttp.send();
      }
      
      function got_new_message_count(){
          if (xmlhttp.readyState==4){
              if(xmlhttp.responseText != 'CLOSE') {
                  updateMessageCount(xmlhttp.responseText);
              }
              //...
              poll_new_messages();
          }
      }
      

      现在,无论如何,PHP 都会在 30 秒内返回响应。如果您在页面上使用停留,并且您收到 CLOSE,您只是不更新​​页面上的计数,然后重新询问。

      如果用户移动到新页面,您的 PHP 实例将在 30 秒内停止循环,并返回响应。但是,在新页面上,关心该连接的 XHR 不再存在,因此它不会启动 另一个 循环。

      【讨论】:

      • 感谢您的回答。我的 ajax 函数 poll_new_messages() 有一个响应处理程序 got_new_message_count() 函数。响应处理程序在触发时调用 poll_new_messages()。因此,在这两种情况下,我都会得到相同数量的无限循环或没有响应处理函数(如果在 CLOSE 上,我不会再次调用 poll_new_messages)。
      • 在 Javascript 中,您可以检查“CLOSE”,如果是,请重新投票。否则,显示计数。如果用户更改页面,当循环超时时,它不会从您的处理程序重新启动,因为处理程序不再监听(从该页面)。
      • 当另一个 $newMessageCount 再次到达时会发生什么,没有处理此事件的处理程序?您提出的解决方案只会通知用户一次(以防用户不会更改页面并再次开始长轮询)。
      • 编辑显示你可以在 JS 中做什么,并试图更好地解释我的 cmets 中的所有内容
      • 如果 X 用 set_time_limit(X) 设置,长轮询请求在 X 秒后超时。所以这不是问题。在我看来,长轮询的想法是让它超过 30 秒,即使只有 30 秒,我更喜欢为每个用户运行一个线程,而不是每个页面切换一个线程。
      【解决方案3】:

      您可以尝试定期检查connection_aborted()。请注意,connection_aborted() 在您编写一些输出并完成flush() 之前,可能不会意识到连接实际上已被中止。

      事实上,仅仅周期性地产生一些输出可能就足以让 php 注意到连接自行关闭,并自动终止你的脚本。

      【讨论】:

      • 感谢您的回答。每次我回显一些数据时,这个解决方案不会触发 ajax 响应功能吗?使此解决方案成为常规轮询而不是长时间轮询。
      猜你喜欢
      • 2010-12-31
      • 1970-01-01
      • 2013-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-03
      • 2011-08-17
      • 2011-08-15
      相关资源
      最近更新 更多