首页 文章

服务器发送的事件和php - 什么触发服务器上的事件?

提问于
浏览
66

所有,

HTML5 Rocks有一个很好的关于服务器发送事件(SSE)的初学者教程:

http://www.html5rocks.com/en/tutorials/eventsource/basics/

但是,我不明白一个重要的概念 - 什么触发服务器上导致消息发送的事件?

换句话说 - 在HTML5示例中 - 服务器只发送一次时间戳:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

如果我正在构建一个实际示例 - 例如,Facebook风格的“墙”或股票代码,其中服务器会在每次某些数据更改时将“新”消息“推送”到客户端,那么它是如何工作的?

换句话说...... Does the PHP script have a loop that runs continuously, checking for a change in the data, then sending a message every time it finds one? If so - how do you know when to end that process?

或者 - PHP脚本是否只是发送消息,然后结束(如HTML5Rocks示例中的情况)?如果是这样 - 你如何获得持续更新?浏览器是否只是定期轮询PHP页面?如果是这样 - 那是一个“服务器发送的事件”?这与使用AJAX定期调用PHP页面的JavaScript中编写setInterval函数有什么不同?

对不起 - 这可能是一个非常天真的问题 . 但是我找不到的例子都没有说清楚 .

[UPDATE]

我认为我的问题措辞不多,所以这里有一些澄清 .

假设我有一个网页,应该显示Apple股票的最新价格 .

当用户首次打开页面时,该页面会创建一个EventSource,其URL为我的“stream” .

var source = new EventSource('stream.php');

我的问题是 - “stream.php”应该如何工作?

像这样? (伪代码):

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
    function sendMsg($msg) {
        echo "data: $msg" . PHP_EOL;
        echo PHP_EOL;
        flush();
    }

    while (some condition) {
        // check whether Apple's stock price has changed
        // e.g., by querying a database, or calling a web service
        // if it HAS changed, sendMsg with new price to client
        // otherwise, do nothing (until next loop)
        sleep (n) // wait n seconds until checking again
    }
?>

换句话说 - 只要客户端“连接”它,“stream.php”是否保持打开状态?

如果是这样的话 - 这是否意味着您拥有与并发用户一样多的运行 stream.php 的线程?如果是这样 - 是远程可行的,还是构建应用程序的适当方式?你怎么知道何时可以结束 stream.php 的实例?

我天真的印象是,如果是这样的话,PHP isn't 适合这种服务器的技术 . 但是我所有的演示都让我感到困惑......

5 回答

  • 27

    服务器发送的事件用于从服务器端到客户端的实时更新 . 在第一个示例中,不保留来自服务器的连接,并且客户端每3秒尝试再次连接,并使服务器发送的事件与ajax轮询没有区别 .

    因此,要使连接保持不变,您需要将代码包装在循环中并不断检查更新 .

    PHP是基于线程的,更多连接的用户将使服务器耗尽资源 . 这可以通过控制脚本执行时间来解决,并在脚本超过一定时间(即10分钟)时结束脚本 . EventSource API将再次自动连接,因此延迟在可接受的范围内 .

    另外,查看我的PHP library for Server-sent events,您可以更多地了解如何在PHP中执行服务器发送的事件并使其更容易编码 .

  • -4

    “......”“stream.php”只要客户端“连接”它就会保持打开状态?“

    是的,你的伪代码是一种合理的方法 .

    “你怎么知道何时可以结束stream.php的实例?”

    在最典型的情况下,当用户离开您的站点时会发生这种情况 . (Apache识别关闭的套接字,并杀死PHP实例 . )从服务器端关闭套接字的主要时间是你知道一段时间内没有数据;您发送给客户的最后一条消息是告诉他们在某个时间回来 . 例如 . 在您的股票流媒体案例中,您可以在晚上8点关闭连接,并告诉客户在8小时内回来(假设NASDAQ从凌晨4点到晚上8点开放报价) . 星期五晚上你告诉他们星期一早上回来 . (我有一本关于SSE的新书,并在这个主题上专门介绍了几个部分 . )

    “......如果是这种情况,PHP对于这种服务器来说不是一种合适的技术 . 但到目前为止我看到的所有演示都暗示PHP对此很好,这就是为什么我是如此迷茫...”

    好吧,人们认为PHP不适合普通的网站,而且它们是正确的:如果你用C替换整个LAMP堆栈,你可以用更少的内存和CPU周期来做 . 然而,尽管如此,PHP还是为大多数网站提供了很好的功能 . 它是一种非常高效的Web工作语言,因为它结合了熟悉的类C语法和如此多的库,并且对于管理人员而言是一种令人欣慰的语言,因为大量的PHP程序员可以雇用,有大量的书籍和其他资源,还有一些大的用例(例如Facebook和维基百科) . 这些与您选择PHP作为流媒体技术的原因基本相同 .

    典型的设置不是每个PHP实例与NASDAQ的一个连接 . 相反,您将拥有另一个与NASDAQ连接的进程,或者可能是从群集中的每台计算机到NASDAQ的单个连接 . 然后将价格推入SQL / NoSQL服务器或共享内存 . 然后,PHP只会轮询共享内存(或数据库),并将数据推出 . 或者,有一个数据收集服务器,每个PHP实例打开一个与该服务器的套接字连接 . 数据收集服务器在收到每个PHP客户端时会推送更新,然后他们将这些数据推送到客户端 .

    使用Apache PHP进行流式传输的主要可伸缩性问题是每个Apache进程的内存 . 当您达到硬件的内存限制时,做出业务决策,将另一台计算机添加到集群,或者将Apache从循环中删除,并编写专用的HTTP服务器 . 后者可以用PHP完成,因此您可以重用所有现有知识和代码,或者您可以用另一种语言重写整个应用程序 . 我的纯开发人员会在C中编写一个专用的,简化的HTTP服务器 . 我的经理会添加另一个盒子 .

  • 4

    我注意到sse techink将每一个延迟数据发送到客户端(类似于从客户端页面e.x.Ajax池化数据中反转池化数据技术) . 所以为了克服这个问题,我在sseServer.php页面上做了这个:

    <?php
            session_start();
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache'); // recommended to prevent caching of event data
            require 'sse.php';
            if ($_POST['message'] != ""){
                    $_SESSION['message'] = $_POST['message'];
                    $_SESSION['serverTime'] = time();
            }
            sendMsg($_SESSION['serverTime'], $_SESSION['message'] );
    ?>
    

    而sse.php是:

    <?php
    function sendMsg($id, $msg) {
      echo "id: $id" . PHP_EOL;
      echo "data: $msg" . PHP_EOL;
      echo PHP_EOL;
      ob_flush();
      flush();
    }
    ?>
    

    请注意,在sseSerer.php中,我启动会话并使用会话变量!克服这个问题 .

    我每次想要"update"消息时,我都会通过Ajax调用sseServer.php(发布并设置值为 variable message ) .

    现在在jQuery(javascript)我做类似的事情:1st)我声明一个全局变量 var timeStamp=0; 2nd)我使用下一个算法:

    if(typeof(EventSource)!=="undefined"){
            var source=new EventSource("sseServer.php");
            source.onmessage=function(event)
            if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){
                    /* this is initialization */
                    timeStamp=event.lastEventId;
                    $.notify("Please refresh "+event.data, "info");
            } else {
                    if (timeStamp==0){
                             timeStamp=event.lastEventId;
                    }
            } /* fi */
    
    } else {
            document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
    } /* fi */
    

    在以下行: $.notify("Please refresh "+event.data, "info"); 是否可以处理该消息 .

    对于我的情况,我曾经发送过jQuery通知 .

    您可以使用POSIX PIPES或DB Table来通过POST传递“消息”,因为sseServer.php执行类似“无限循环”的操作 .

    我当时的问题是,上面的代码并没有向所有客户端发送“消息”,而只向该对发送消息(调用sseServer.php的客户端作为每个客户端的个体),因此我将更改技术并更改为数据库更新从我想要触发“消息”的页面然后sseServer.php而不是通过POST获取消息它将从数据库表获取它 .

    我希望我有所帮助!

  • 3

    这实际上是关于您的应用程序的结构性问题 . 实时事件是您从一开始就想要考虑的事情,因此您可以围绕它设计应用程序 . 如果你编写了一个只使用字符串查询运行一堆随机 mysql(i)_query 方法的应用程序,并且可以选择重写大部分应用程序,或者进行持续的服务器端轮询 .

    但是,如果您将实体作为对象进行管理并通过某种中间类传递它们,则可以挂钩该过程 . 看看这个例子:

    <?php
    class MyQueryManager {
        public function find($myObject, $objectId) {
            // Issue a select query against the database to get this object
        }
    
        public function save($myObject) {
            // Issue a query that saves the object to the database
            // Fire a new "save" event for the type of object passed to this method
        }
    
        public function delete($myObject) {
            // Fire a "delete" event for the type of object
        }
    }
    

    在您的应用程序中,当您准备保存时:

    <?php
    $someObject = $queryManager->find("MyObjectName", 1);
    $someObject->setDateTimeUpdated(time());
    $queryManager->save($someObject);
    

    这不是最优雅的例子,但它应该作为一个体面的构建块 . 您可以挂钩到实际的持久层以处理触发这些事件 . 然后你可以立即得到它们(尽可能实时)而无需锤击你的服务器(因为你不需要经常查询你的数据库,看看是否有变化) .

    您显然不会以这种方式捕获对数据库的手动更改 - 但如果您以任何频率手动对数据库执行任何操作,则应该:

    • 修复需要您进行手动更改的问题

    • 构建一个工具来加速进程,并触发这些事件

  • 20

    基本上,PHP不适用于此类事物 . 是的,你可以使它工作,但它将是一个灾难高负荷 . 我们运行通过websockets将库存变化信号发送到数十个用户的股票服务器 - 如果我们使用php那么......好吧,我们可以,但那些自制周期 - 只是一场噩梦 . 每个连接都将在服务器上创建一个单独的进程,或者您必须处理来自某种数据库的连接 .

    只需使用nodejs和socket.io即可 . 它可以让您轻松启动并在几天内运行服务器 . Nodejs也有自己的局限性,但对于websockets(和SSE)连接,它现在是最强大的技术 .

    而且 - SSE并不像看起来那么好 . websockets的唯一优势 - 数据包是本机gzip(ws不是gzip),但缺点是SSE是单端连接 . 您的用户,如果他想将另一个股票代码添加到子标签,则必须发出ajax请求(包括原始控制的所有问题,请求将会很慢) . 在websockets中,客户端和服务器在一个单独的打开连接中进行双向通信,因此如果用户发送交易信号或订阅报价,他只需在已打开的连接中发送一个字符串 . 它很快 .

相关问题