【问题标题】:PHP infinitive loop or jQuery setInterval?PHP无限循环或jQuery setInterval?
【发布时间】:2012-01-28 01:35:15
【问题描述】:

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 更好的解决方案,该方法连接到 PHP 方法,如果每 1 秒返回消息(假设 1 秒每 5 秒增加 +0.25),则返回消息?

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

那么,哪种解决方案更好(更重要的是,它占用的资源更少?)。因为会有数百个这样的聊天。

另外,第一个解决方案会导致问题吗?假设我会重新加载一个页面,或者我会每 30 秒停止一次执行,这样我就不会得到 502 Bad Gateway。

编辑:我相信第二种解决方案更好,所以我将重新实现我的网站,但我只是好奇这是否会给用户带来问题?会发生意想不到的事情吗? 我注意到的第一个问题是,在至少有一条新消息之前,您无法转到其他页面。

【问题讨论】:

    标签: php jquery mysql memcached comet


    【解决方案1】:

    首先,问问自己是否有必要经常更新聊天。会发生什么类型的聊天?是实时的吗?简单的问答?技术支持?等等。在除实时聊天之外的所有情况下,最好使用基于 JS 的长轮询设计,因为即时响应并不那么重要。如果这是用于实时聊天,那么您应该考虑类似 Gmail 的设计,在该设计中保持 XHR 打开并在收到消息时将消息推送回客户端。如果连接资源是一个问题,您可以通过使用间隔非常短(例如 5-10 秒)的长轮询来获得。

    【讨论】:

      【解决方案2】:

      我写了一篇关于如何处理类似问题的博客文章(使用 node.js,但原则适用)。 http://j-query.blogspot.com/2011/11/strategies-for-scaling-real-time-web.html

      我的建议是,如果它会很大 a) 你需要在你的 web 服务器层上疯狂地缓存,这可能意味着你的 AJAX 调用需要有一个时间戳或者 b) 使用类似 socket.io 的东西,专为扩展实时网络应用程序而构建,并内置了对渠道的支持。

      【讨论】:

        【解决方案3】:

        php 中的无限循环可以并且将使用 100% 的 CPU。睡眠功能将解决这个问题。但是,您可能不希望为连接到服务器的每个客户端始终运行一个单独的 HTTP 进程,因为您将用完连接。您可以只有一个 php 进程来查看所有入站消息并在它们进入时将它们路由到正确的人。这个进程可以从 cron 作业中启动,每分钟一次。我已经多次写过这种类型的东西,它就像一个魅力。 注意:如果进程已经在运行,请确保不要运行该进程,否则会遇到多处理问题(例如收到双重消息)。换句话说,你需要让进程线程安全。

        如果您想进行实时聊天,那么您可能需要查看StreamHub,它会打开与客户端浏览器的全双工连接。

        【讨论】:

        • 如果你坚持使用PHP,这就是正确的答案。然而,PHP 并非设计为聊天服务器,因此您应该考虑其他解决方案,例如 Node.js。然而,PHP 非常适合做网站。
        【解决方案4】:

        聊天是一对多的通信,而每个人都可以发送消息,也可以接收其他人的消息。

        这两个动作(发送、接收)连续发生。所以这看起来像是一个无限循环,而用户可以进入(加入聊天)和退出(离开聊天)。

        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 有一种 memcache 桥,它应该使存储操作更加快速和方便)。

        读取事务脚本同样简单,它只会读取所有 ID 高于传递给它的消息并将其返回给客户端。

        使这些脚本尽可能简单并优化存储的读/写时间,因此它们可以快速执行,即使通过普通 HTTP 聊天也可以完成。

        您的网络服务器和整体互联网连接可能仍然不够快(尽管有keep-alive)。

        但是,目前 HTTP 应该足以测试您的聊天系统是否真的在没有任何循环、客户端或服务器端的情况下工作。

        让服务器保持简单也很好,因为每个客户端都依赖它们,所以他们应该只做自己的工作,仅此而已。

        您可以随时更改可以与您的聊天客户端交互的服务器(或提供不同类型的服务器),方法是为聊天客户端提供不同的发送和接收功能实现。例如。我在您的问题中看到您正在使用彗星,这应该也可以,直接为彗星实现服务器可能很容易。

        如果将来 websocket 更易于访问(出于安全考虑,可能永远不会出现这种情况),您也可以为 websocket 提供另一种类型的服务器。只要流的数据结构完好无损,这将适用于彼此相邻的不同类型的服务器。数据库将负责一致性。

        希望这有帮助。


        作为附加说明:HTML5 提供了一个名为 Stream Updates with Server-Sent Events 的东西,带有 online demoPHP/JS sources。 HTML 5 功能已经在 javascript 中提供了一个事件对象,可用于创建示例性聊天客户端传输实现。

        【讨论】:

        • +1,这是(到目前为止)唯一的答案,它不仅建议了一种不同的无限循环方法,而且还提供了有关如何创建基于 Web 的全双工(或模拟)消息交换的信息.
        • +1 只是因为谁写了这么多?!我的意思是,我读过更短的宣言。我去过 DMV 的时间较短。我和 2 岁的双胞胎吃过更短的早餐。除了开玩笑,还有很好的细节和想法!
        【解决方案5】:

        现在它不是 PHP 或 jQuery 任务。节点.js! 有socket.io,意思是WebSockets。

        我会解释为什么 node.js 更好。我的任务是每隔 10 秒刷新一次页面标记。我已经用第一种方法完成了。当持久用户数达到 200 时。Http 服务器和 php 都遇到了麻烦。有很多不必要的要求。

        Node.js 给了你什么:

        • 为聊天创建单独的房间 (here)
        • 只为有更新的人发送数据(例如,如果我没有任何新消息,当数据库中有选择时,我的刷新将被阻止)
        • 无论有多少用户,您每 0.5 秒对数据库运行 1 次查询

        看看 Node.js 和 Socket.io。这个解决方案对我有很大的帮助。

        【讨论】:

          猜你喜欢
          • 2012-11-06
          • 1970-01-01
          • 1970-01-01
          • 2013-06-02
          • 1970-01-01
          • 2015-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多