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

时间:2013-01-28 15:07:29

标签: php server-sent-events

所有

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风格的“墙”或股票代码,服务器会在每次某些数据更改时“推送”新客户的消息,那么那工作?

换句话说...... PHP脚本是否有一个连续运行的循环,检查数据的变化,然后每次发现一个消息时发送一条消息?如果是这样 - 你怎么知道何时结束这个过程?

或者 - 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 不是适用于此类服务器的技术。但到目前为止我看过的所有演示都暗示PHP对此很好,这就是为什么我很困惑......

5 个答案:

答案 0 :(得分:27)

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

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

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

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

答案 1 :(得分:22)

  

“......只要客户端”连接“,”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服务器。我的经理会添加另一个盒子。

答案 2 :(得分:4)

我注意到sse techink将每一个延迟数据发送到客户端(比如从客户端页面转换池化数据技术链接,比如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中我启动会话并使用会话变量!克服这个问题。

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

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

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的客户端作为每个客户端的个人),所以我将改变技术从我要触发“消息”的页面更新数据库,然后通过POST获取消息,然后sseServer.php将从DB表中获取消息。

我希望我有所帮助!

答案 3 :(得分: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);

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

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

  • 解决需要您进行手动更改的问题
  • 构建工具以加快流程并解除这些事件

答案 4 :(得分:-6)

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

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

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