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

所有人,

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

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

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

In other words - in the HTML5 example - the server simply sends a timestamp 一次:

<?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 页面)有什么不同?

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

[更新]

我觉得我的问题措辞不当,所以我来澄清一下。

Let's say I have a web page that should display the most recent price of Apple's stock.

当用户首次打开该页面时,该页面使用我的“流”的 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的线程数量与拥有并发用户的线程数量一样多?如果是这样的话——这是否远程可行,或者是否是构建应用程序的合适方法?你怎么知道什么时候你可以 一个 stream.php的实例?

我的天真印象是,如果是这种情况,PHP 不是是适合这种服务器的技术。但是到目前为止我看到的所有演示都暗示 PHP 适合这种情况,这就是为什么我如此困惑..。

65108 次浏览

这实际上是一个关于应用程序的结构性问题。实时事件是您希望从一开始就考虑的事情,因此您可以围绕它设计应用程序。如果您编写的应用程序只是使用字符串查询运行一堆随机的 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秒重新连接一次,这使得服务器发送的事件与 ajax 轮询没有区别。

So, to make the connection persist, you need to wrap your code in a loop and check for updates constantly.

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

Also, check out my 用于服务器发送事件的 PHP 库, you can understand more about how to do server-sent events in PHP and make it easier to code.

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

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

“那么如何知道何时可以终止 stream. php 的实例呢?”

在最典型的情况下,这发生在用户离开站点时。(Apache 识别关闭的套接字,并杀死 PHP 实例。)您可能从服务器端关闭套接字的主要时间是如果您知道将有一段时间没有数据; 您发送给客户端的最后一条消息是告诉他们在某个时间返回。例如,在你的股票流媒体案例中,你可以在晚上8点关闭连接,并告诉客户8小时后再来(假设纳斯达克在早上4点到晚上8点之间开放报价)。星期五晚上你告诉他们星期一早上再来。(我有一本即将出版的关于 SSE 的书,并且就这个主题专门写了一些章节。)

如果是这样的话,PHP 不是一种适合这种情况的技术 但是到目前为止我看到的所有演示都暗示 PHP 是 just fine for this, which is why I'm so confused..."

人们认为 PHP 不适合于普通的网站,他们是对的: 如果用 C + + 代替整个 LAMP 堆栈,那么可以用更少的内存和 CPU 周期完成。然而,尽管如此,PHP 还是为大多数站点提供了很好的支持。它是一种非常高效的网络工作语言,因为它结合了熟悉的 C-like 语法和许多库,并且对于管理人员来说是一种安慰,因为他们可以雇佣大量的 PHP 程序员,有大量的书籍和其他资源,还有一些大的用例(例如 Facebook 和 Wikipedia)。这些基本上与您可能选择 PHP 作为流技术的原因相同。

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

使用 Apache + PHP 进行流处理的主要可伸缩性问题是每个 Apache 进程的内存。当您达到硬件的内存限制时,作出将另一台机器添加到集群的业务决策,或者将 Apache 排除在循环之外,并编写一个专用的 HTTP 服务器。后者可以在 PHP 中完成,这样您现有的所有知识和代码都可以重用,或者您可以用另一种语言重写整个应用程序。作为纯粹的开发人员,我会用 C + + 编写一个专用的、流线型的 HTTP 服务器。我的经理会再加一个盒子。

I have notice that the sse techink sends every couple of delay data to the client (somtething like reversing the pooling data techink from client page e.x. Ajax pooling data.) so to overcome this problem i made this at a sseServer.php page :

<?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();
}
?>

Notice that at the sseSerer.php i start a session and using a session variable! to overcome the problem.

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

现在在 jQuery (javascript)中,我做了类似的事情: 我声明了一个全局变量 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 的工作作为个人的每一对) ,所以我会改变技术和数据库更新从页面,我想触发“消息”,然后 sseServer.php 通过 POST 获取消息,它将得到它从数据库表。

我希望我有帮助!

基本上,PHP 不适合这类事情。 是的,你可以让它工作,但它将是一个灾难高负荷。我们运行的股票服务器通过网络套接字向成千上万的用户发送股票交易信号——如果我们用 php 的话... ... 好吧,我们可以,但是那些自制的周期——只是一场噩梦。每个连接都会在服务器上形成一个独立的进程,或者必须处理来自某种数据库的连接。

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

And also - SSE is not that good as it seems. The only advantage to websockets - is that packets are being gzipped natively (ws is not gzipped), but on the downside is that SSE is one-side connection. You user, if he wants to add another stock symbol to subscripton, will have to make ajax request (including all troubles with origin control and the request will be slow). In websockets client and sever communicate both ways in one single opened connection, so if user sends a trading signal or subscribes to quote, he just send a string in already opened connection. And it's fast.