PHP无限循环还是jQuery setInterval?

5

Js:

<script>
function cometConnect(){
    $.ajax({
          cache:false,
          type:"post",
          data:'ts='+1,
          url: 'Controller/chatting',
          async: true,
          success: function (arr1) {
              $(".page-header").append(arr1);
          },
          complete:function(){
            cometConnect(true);
            nerr=false;
          },
          dataType: "text"
        }); 
}
cometConnect();
</script>

Php:

public function chatting()
{
    while(true)
    {
       if(memcache_get(new_message))
          return new_message; 
       sleep(0.5);
    }
}

这是比设置setInterval更好的解决方案吗?setInterval连接到PHP方法,每隔1秒返回消息(假设每5秒增加0.25秒)。

如果使用第一个解决方案,我可能可以使用sleep(0.5),它会立即给我信息,因为php循环很便宜,不是吗?

那么,哪个解决方案更好(更重要的是,哪个占用更少的资源?)。因为将会有数百个类似于此的聊天。

此外,第一个解决方案会引起问题吗?例如,我重新加载页面或每30秒停止执行以避免502 Bad Gateway。

编辑:我认为第二个解决方案更好,所以我打算重新实现我的网站,但我只是好奇这会对用户造成问题吗?会发生意外的事情吗?我注意到的第一个问题是,在有至少一条新消息之前,您无法转到其他页面。

5个回答

9

聊天是一种一对多的通讯方式,每个人都可以发送消息并从其他所有人接收消息。

这两个动作(发送、接收)会持续发生。因此看起来像一个无限循环,用户可以进入(加入聊天)并退出(离开聊天)。

  1. 进入
  2. 发送消息
  3. 接收消息
  4. 退出

因此,客户端的循环看起来像这样(伪代码):

while (userInChat)
{
    if (userEnteredMessages)
    {
        userSendMessages(userEnteredMessages)
    }
    if (chatNewMessages)
    {
        displayMessages(chatNewMessages)
    }
}

正如您在问题中已经指出的那样,问题在于为网站实现这种类型的聊天。

要为网站实现这样的“循环”,首先面临的情况是不希望在此处有实际的循环。只要用户在聊天中,它就会一直运行下去。因此,您希望将循环的执行时间分配到时间上。

为此,您可以将其转换为事件函数的集合:

ChatClient
{
    function onEnter()
    {
    }
    function onUserInput(messages)
    {
        sendMessages = send(messages)

        display(sendMessages)
    }
    function onReceive(messages)
    {
        display(messages)
    }
    function onExit()
    {
    }
}

现在可以通过触发事件而不是循环来实现。唯一剩下的就是随着时间的推移触发这些事件的实现,但目前这并不是真正有趣的,因为它将取决于聊天数据交换的实际实现方式。
总会有一个远程点,聊天客户端(以某种方式)连接到该点,以发送自己的消息并接收新消息。
这是一种聊天消息流。再次强调,这看起来像一个循环,但实际上它是一个流。就像在聊天客户端的循环中一样,在某个时间点上,它将钩入该流并将从该流发送输入(写入)和接收输出(读取)。
这已经在上面的ChatClient伪代码中可见,当用户输入一个或多个消息时,会触发一个事件,然后将其发送(写入)。并且onReceive事件函数中将提供已读取消息。
作为流数据,需要有顺序。由于这一切都是基于事件的,并且有多个客户端可用,因此需要一些专门的处理。由于顺序是相对的,它只能在其上下文中起作用。上下文可以是时间(一条消息在另一条消息之前到达),但如果聊天客户端具有与服务器或其他客户端不同的时钟,则无法使用现有时钟作为消息顺序的时间源,因为它通常在WAN中的计算机之间有所不同。
相反,您可以创建自己的时间来排列所有消息。通过在所有客户端和服务器之间共享时间,可以实现有序的流。这可以通过仅在一个中央位置对消息进行编号来轻松完成。幸运的是,您的聊天有一个中央位置——服务器。
消息流从第一条消息开始,以最后一条消息结束。因此,您只需将第一条消息赋予1号,然后每条新消息都会获得下一个更高的编号。让我们称之为消息ID。
因此,无论您使用哪种服务器技术,聊天都知道两种类型的消息:带有ID的消息和不带ID的消息。这也代表了消息的状态:要么不是流的一部分,要么是流的一部分。

未关联流的消息是用户已经输入但尚未发送到服务器的消息。当服务器接收到“空闲”消息时,它可以通过分配ID将其放入流中:

    function onUserInput(messages)
    {
        sendMessages = send(messages)

        display(sendMessages)
    }

作为伪代码示例所示,这里正在发生的事情。onUserInput事件获取尚未成为流的一部分的消息。sendMessages例程将返回它们的流表示形式,然后显示。
显示例程能够按其流顺序显示消息。
因此,无论客户端/服务器通信如何实现,使用这样的结构,您实际上可以粗略处理基于消息的聊天系统并将其与底层技术解耦。
服务器需要做的唯一事情就是接收消息,为每个消息分配一个ID并返回这些ID。 ID的分配通常在服务器将消息存储到其数据库中时完成。良好的数据库会确保正确编号消息,因此没有太多要做。
另一个交互是从服务器读取新消息。为了有效地通过网络执行此操作,客户端告诉服务器它想要从哪个消息开始阅读。然后,服务器将自那时(ID)起传递消息给客户端。
如此展示,从一开始的“无限循环”,现在已经变成了一个基于事件的系统,具有远程调用功能。由于远程调用很昂贵,最好能够使它们能够通过一个连接传输更多的数据。其中一部分已经在伪代码中了,因为可以向服务器发送一个或多个消息,并同时接收来自服务器的零个或多个消息。
理想的实现是拥有一个连接到服务器的全双工通信,允许读写消息。然而,目前尚不存在这样的JavaScript技术。这些东西正在通过Websockets和Webstream API等进行开发,但暂时让我们简单地看看我们拥有的:无状态HTTP请求、服务器上的一些PHP以及MySQL数据库。
消息流可以表示为一个数据库表,该表具有自动递增的唯一键作为ID,以及其他字段来存储消息。
写入事务脚本将只连接到数据库,插入消息并返回ID。这是一个非常常见的操作,应该很快(mysql有一种类似于内存缓存桥的东西,应该使存储操作更加快速和方便)。
读取事务脚本同样简单,它将只读取所有ID高于传递给它的消息,并将其返回给客户端。
保持这些脚本尽可能简单,并优化读写存储时间,以便它们可以快速执行,即使在使用纯HTTP聊天时也能完成。
尽管有keep-alive,但您的Web服务器和整体互联网连接可能仍然不够快。
然而,暂时使用HTTP应该足以测试您的聊天系统是否正常工作,而没有任何循环,无论是客户端还是服务器端。
保持服务器简单也很重要,因为每个客户端都依赖于它们,所以它们只需要完成自己的工作就行了。
您可以随时更改服务器(或提供不同类型的服务器),通过给聊天客户端不同的发送和接收函数实现与聊天客户端交互。例如,我在您的问题中看到您正在使用comet,这也应该可以工作,直接实现comet服务器可能很容易。
如果将来Websockets更易于访问(由于安全考虑可能永远不会发生),您也可以为Websockets提供另一种类型的服务器。 只要流的数据结构完好无损,这将与其他类型的服务器并存。 数据库将负责协调。
希望这有所帮助。

额外提醒:HTML5 提供了一项名为 服务器推送事件(Server-Sent Events) 的功能,同时提供了一个 在线示例PHP/JS 源代码。该 HTML 5 功能已经在 JavaScript 中提供了一个事件对象,可用于创建示例聊天客户端传输实现。


1
+1,这是目前为止唯一一个不仅建议使用不同的无限循环方法,而且还提供有关如何创建基于Web的全双工(或模拟)消息交换的信息的答案。 - N.B.
1
+1只是因为这篇文章写的够多!我的意思是,我读过比这更短的宣言。我去车管所的路程也更短。我和两岁双胞胎共进的早餐时间也更短。开玩笑的,不过,很好的细节和想法! - iND

5
在php中无限循环可能会占用100%的CPU。Sleep函数可以解决这个问题。然而,你可能不想为每个连接到服务器的客户端运行一个单独的HTTP进程,因为你会耗尽连接。你可以只有一个php进程,它查看所有传入的消息并将它们路由到正确的人员。这个过程可以从cron作业启动一分钟一次。我已经多次编写了这种类型的程序,它像魅力一样工作。注意:确保不要在进程已经运行时再次运行该进程,否则你将遇到多处理问题(如收到重复消息)。换句话说,你需要使进程线程安全。

如果你想要实时聊天,那么你可能需要看一下StreamHub,它打开了与客户端浏览器的全双工连接。


如果你坚持使用 PHP,那么这就是正确的答案。然而,PHP 并不是为了成为聊天服务器而设计的,因此你应该考虑其他解决方案,比如 Node.js。但是对于制作网站来说,PHP 是非常棒的选择。 - King Friday

5
现在不是PHP或jQuery的任务了,是Node.js!有socket.io,这意味着WebSockets。
我将解释为什么node.js更好。我有一个任务,每10秒刷新页面上的标记。我用第一种方法完成了它。当持续用户数量达到200时,Http服务器和php出现了问题。有很多不必要的请求。
Node.js给你什么:
- 为聊天创建单独的房间(here) - 仅向更新数据的人发送数据(例如,如果我没有新消息,当从数据库中选择时,我的刷新将被阻止) - 每0.5秒运行1个查询到DB,无论有多少用户
只需查看Node.js和Socket.io。这个解决方案帮助我大大提高了效率。

5
我写了一篇关于如何解决类似问题的博客文章(使用node.js,但原则适用)。 http://j-query.blogspot.com/2011/11/strategies-for-scaling-real-time-web.html 我的建议是:如果将要扩展,需要在Web服务器层大量缓存,这可能意味着您的AJAX调用需要有一个时间戳;或者使用像socket.io这样的东西,它专为扩展实时Web应用程序而构建,并具有对通道的内置支持。

2
首先,问问自己是否有必要频繁更新聊天内容。聊天类型是什么?实时的吗?简单的问答?技术支持?等等。除了实时聊天之外,在其他情况下最好使用基于长轮询的JS设计,因为即时响应并不重要。如果这是用于实时聊天,则应考虑像Gmail那样的设计,通过保持XHR打开状态,并在接收到消息时将其推送回客户端。如果连接资源是一个问题,可以使用很短的间隔(例如5-10秒)进行长轮询。

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