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


91

所有,

HTML5 Rocks提供了有关服务器发送事件(SSE)的不错的初学者教程:

http://www.html5rocks.com/zh-CN/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页面?如果是这样-那“服务器发送事件”如何?这与用JavaScript编写setInterval函数有什么不同,该函数使用AJAX定期调用PHP页面?

抱歉-这可能是一个非常幼稚的问题。但是我找不到的所有例子都没有说明这一点。

[更新]

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

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

当用户第一次打开页面时,页面会创建一个带有我的“流” URL的EventSource。

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对此非常合适,这就是为什么我如此困惑的原因...


这就是开发人员必须自己编写代码的部分。获取数据的方式是透过WebSocket /长轮询等,但诀窍是什么触发事件。就个人而言,我已经尝试了一些方法和一种方法,我喜欢(但不是那个故障安全)是在制作MySQL触发一个控制台程序,每次的东西插在一个特定的表。控制台程序将接收有关更改/插入记录的信息,并通过WebSockets将通知发送给相应的用户。基本上,我有一个PHP守护程序正在等待发送消息。
NB

与此相关的一个问题是IE不支持SSE:-/另外,我也会阅读此prodigyproductionsllc.com/articles/programming/javascript/…我认为他正在使用端口来避免孩子过多的问题,但总体上看起来像他的建议避免上证所。国际海事组织,看起来麻烦多于其应有的价值。
PJ Brunet

IE11或Android浏览器目前不支持caniuse.com/eventsource
PJ Brunet


4
我有同样的问题,我想我深深明白你的意思是什么触发服务器上的事件...。当您创建的对象时EventSource('stream.php'),客户端会打开一个连接,stream.php就像通过ajax调用它一样。只要您的服务器端代码有话要说,连接就会触发您的服务器端代码并​​保持连接打开。然后,连接关闭,经过短暂的延迟(我认为在chrome中为3秒),客户端重新打开了连接,这stream.php再次触发了您的文件。
艾哈迈德·马勒基

Answers:


28

“ ...只要客户端“连接”到它,“ stream.php”是否保持打开状态?”

是的,您的伪代码是一种合理的方法。

“而且您怎么知道何时可以终止stream.php的实例?”

在最典型的情况下,这种情况发生在用户离开您的网站时。(Apache识别了关闭的套接字,并杀死了PHP实例。)您可能主要是从服务器端关闭套接字的主要时间是,如果您知道一段时间内将没有数据,那么可以关闭该套接字。您发送给客户的最后一条消息是告诉他们在特定时间回来。例如,在进行股票流传输的情况下,您可以在晚上8点关闭连接,并告诉客户在8小时内回来(假设纳斯达克在凌晨4点至晚上8点开放报价)。星期五晚上,您告诉他们星期一早上回来。(我有一本即将出版的关于SSE的书,并专门讨论了该主题的两个部分。)

“ ...如果是这种情况,PHP不适用于这种服务器。但是到目前为止,我看过的所有演示都暗示PHP对此非常合适,这就是为什么我如此困惑...”

好吧,人们认为PHP不是适用于普通网站的技术,而且它们是对的:如果用C ++替换整个LAMP堆栈,则可以用更少的内存和CPU周期来做到这一点。但是,尽管如此,PHP仍然可以很好地支持大多数站点。由于结合了类似C的语法和如此众多的库,因此它是一种对Web工作非常有生产力的语言,它为经理提供了一种令人欣慰的方式,因为它雇用了许多PHP程序员,大量书籍和其他资源,以及一些大型用例(例如Facebook和Wikipedia)。这些基本上就是您可能选择PHP作为流技术的相同原因。

典型的设置不是每个PHP实例仅一个到NASDAQ的连接。取而代之的是,您将拥有另一个连接到NASDAQ的连接,或者从群集中的每台计算机到NASDAQ的单个连接的另一个进程。然后将价格推到SQL / NoSQL服务器或共享内存中。然后,PHP仅轮询该共享内存(或数据库),并将数据推出。或者,拥有一个数据收集服务器,并且每个PHP实例都打开一个到该服务器的套接字连接。数据收集服务器在收到每个PHP客户端更新时将其推送到客户端,然后将这些数据推送至客户端。

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


随着每个Apache连接进程消耗内存,使用Nginx会更好吗?
张巴兹

@ZhangBuzz在我说过Apache + PHP的地方,它的意思是“ Web服务器+ PHP进程”,因此与使用其他Web服务器基本上没有区别。
达伦·库克


另请注意,Nginx可能会更有效,因为使用更少的内存:blog.webfaction.com/2008/12/…–
Enrique

31

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

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

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

此外,请查看我的PHP库以了解服务器发送的事件,您可以了解更多有关如何在PHP中进行服务器发送的事件并使其更易于编写的信息。


5
您能否详细说明“可以通过控制脚本执行时间并在超过一定时间后结束脚本来解决此问题”?如果用户数量过多,由于用户仅需3秒钟即可再次连接,因此关闭连接真的会大大提高资源利用率吗?
路加福音

4

我注意到sse techink将每两个延迟数据发送到客户端(有点像从客户端页面(例如Ajax池数据)反转池数据techink。)因此,为了克服此问题,我在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上,我开始了一个会话并使用了会话变量!克服问题。

variable message每当我想“更新”消息时,我也会通过Ajax调用sseServer.php(将值设置为)。

现在在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或数据库表通过POST传递“消息”,因为sseServer.php的作用类似于“无限循环”。

当时我的问题是上述代码没有将“消息”发送给所有客户端,而是仅发送给该对(称为sseServer.php的客户端对每个对都是单独的),因此我将更改技术并更改为从页面上更新数据库,我想触发“消息”,然后通过sseServer.php来通过POST获取消息,它将从DB表中获取消息。

我希望我能有所帮助!


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);

这不是最优雅的示例,但它应作为一个体面的构建基块。您可以连接到实际的持久层来处理触发这些事件。然后,您可以立即(尽可能实时地)获取它们,而无需花费服务器时间(因为您无需不断查询数据库并查看情况是否发生了变化)。

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

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

3
科林-感谢您的回答。我的错-我的问题不清楚-但这并不是我要问的。我的意思来问这个是......如果你使用PHP作为你的“服务器” -做你从EventSource的在你的客户需要运行调用PHP脚本整个时间的客户端连接到它?这是否意味着,如果您有1,000个并发用户,那么您就有1,000个单独的线程运行1,000个PHP脚本的并发实例?那可行吗?而且,您怎么知道何时结束 php脚本(假设它循环播放以保持“运行状态”)?
mattstuehler 2013年

-7

基本上,PHP不适合用于此类事情。是的,您可以使其正常运行,但这对高负载来说将是一场灾难。我们运行的库存服务器通过websocket向数十个数千用户发送库存变化信号-如果我们要使用php ...那么,我们可以,但是那些自制周期-只是一场噩梦。每个单独的连接都会在服务器上进行单独的处理,或者您必须处理某种数据库的连接。

只需使用nodejs和socket.io。它可以让您轻松启动并在几天之内拥有正在运行的服务器。Node.js也有其自身的局限性,但是对于WebSocket(和SSE)连接而言,它现在是最强大的技术。

而且-SSE看起来并不好。websockets的唯一优点-是将数据包本地压缩(ws不压缩),但不利的是SSE是单边连接。您的用户如果想在下标中添加另一个股票代号,则必须提出ajax请求(包括所有与原点控制有关的麻烦,并且请求速度很慢)。在websockets中,客户端和服务器在一个打开的连接中进行双向通信,因此,如果用户发送交易信号或订阅报价,他只是在已打开的连接中发送字符串。而且速度很快。


2
您可以使用与node.js中的事件循环基本相同的方法来使用React.php。
马捷Koubík

3
尽管可以肯定地说PHP不是最佳选择,但我认为您至少应包括OP要求的内容。
Nishchal Gautam'2
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.