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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14564903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 07:32:52  来源:igfitidea点击:

Server-sent events and php - what triggers events on the server?

phpserver-sent-events

提问by mattstuehler

All,

全部,

HTML5 Rocks has a nice beginner tutorial on Server-sent Events (SSE):

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

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

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

But, I don't understand an important concept - what triggers the event on the server that causes a message to be sent?

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

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

换言之-在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()));

If I were building a practical example - e.g., a Facebook-style "wall" or a stock-ticker, in which the server would "push" a new message to the client every time some piece of data changes, how does that work?

如果我正在构建一个实际示例 - 例如,Facebook 风格的“墙”或股票行情,其中服务器会在每次数据发生变化时向客户端“推送”一条新消息,这是如何工作的?

In other words... 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 脚本是否有一个循环,它会持续运行,检查数据中的变化,然后在每次发现时发送一条消息?如果是这样 - 你怎么知道什么时候结束这个过程?

Or - does the PHP script simply send the message, then end (as appears to be the case in the HTML5Rocks example)? If so - how do you get continuous updates? Is the browser simply polling the PHP page at regular intervals? If so - how is that a "server-sent event"? How is this different from writing a setInterval function in JavaScript that uses AJAX to call a PHP page at a regular interval?

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

Sorry - this is probably an incredibly naive question. But none of the examples I've been able to find make this clear.

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

[UPDATE]

[更新]

I think my question was poorly worded, so here's some clarification.

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

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

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

When the user first opens the page, the page creates an EventSource with the URL of my "stream."

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

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

My question is this - how should "stream.php" work?

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

Like this? (pseudo-code):

像这样?(伪代码):

<?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
    }
?>

In other words - does "stream.php" stay open as long as the client is "connected" to it?

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

If so - does that mean that you have as many threads running stream.phpas you have concurrent users? If so - is that remotely feasible, or an appropriate way to build an application? And how do you know when you can ENDan instance of stream.php?

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

My naive impression is that, if this is the case, PHP isn'ta suitable technology for this kind of server. But all of the demos I've seen so far imply that PHP is just fine for this, which is why I'm so confused...

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

采纳答案by Darren Cook

"...does "stream.php" stay open as long as the client is "connected" to it?"

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

Yes, and your pseudo-code is a reasonable approach.

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

"And how do you know when you can END an instance of stream.php?"

“你怎么知道什么时候可以结束一个stream.php的实例?”

In the most typical case, this happens when the user leaves your site. (Apache recognizes the closed socket, and kills the PHP instance.) The main time you might close the socket from the server-side is if you know there is going to be no data for a while; the last message you send the client is to tell them to come back at a certain time. E.g. in your stock-streaming case, you could close the connection at 8pm, and tell clients to come back in 8 hours (assuming NASDAQ is open for quotes from 4am to 8pm). Friday evening you tell them to come back Monday morning. (I have an upcoming book on SSE, and dedicate a couple of sections on this subject.)

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

"...if this is the case, PHP isn't a suitable technology for this kind of server. But all of the demos I've seen so far imply that PHP is just fine for this, which is why I'm so confused..."

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

Well, people argue that PHP isn't a suitable technology for normal web sites, and they are right: you could do it with far less memory and CPU cycles if you replaced your whole LAMP stack with C++. However, despite this, PHP powers most of the sites out there just fine. It is a very productive language for web work, due to a combination of a familiar C-like syntax and so many libraries, and a comforting one for managers as plenty of PHP programmers to hire, plenty of books and other resources, and some large use-cases (e.g. Facebook and Wikipedia). Those are basically the same reasons you might choose PHP as your streaming technology.

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

The typical setup is not going to be one connection to NASDAQ per PHP-instance. Instead you are going to have another process with a single connection to the NASDAQ, or perhaps a single connection from each machine in your cluster to the NASDAQ. That then pushes the prices into either a SQL/NoSQL server, or into shared memory. Then PHP just polls that shared memory (or database), and pushes the data out. Or, have a data-gathering server, and each PHP instance opens a socket connection to that server. The data-gathering server pushes out updates to each of its PHP clients, as it receives them, and they in turn push out that data to their client.

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

The main scalability issue with using Apache+PHP for streaming is the memory for each Apache process. When you reach the memory limit of the hardware, make the business decision to add another machine to the cluster, or cut Apache out of the loop, and write a dedicated HTTP server. The latter can be done in PHP so all your existing knowledge and code can be re-used, or you can rewrite the whole application in another language. The pure developer in me would write a dedicated, streamlined HTTP server in C++. The manager in me would add another box.

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

回答by Licson

Server-sent events are for realtime update from the server-side to the client-side. In the first example, the connection from the server isn't kept and the client tries to connect again every 3 seconds and makes server-sent events no difference to ajax polling.

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

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

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

PHP is thread-based and more connected users will make the server run out of resources. This can be solved by controlling the script execution time and end the script when it exceed an amount of time (i.e. 10mins). The EventSourceAPI will automatically connect again so the delay is in a acceptable range.

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

Also, check out my PHP library for Server-sent events, you can understand more about how to do server-sent events in PHP and make it easier to code.

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

回答by c.chasapis

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 :

我注意到 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'] );
?>

and the sse.php is :

和 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.

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

Also i call the sseServer.php via Ajax (posting and set value to variable message) every time that i want to "update" message.

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

Now at the jQuery (javascript) i do something like that : 1st) i declare a global variable var timeStamp=0;2nd) i use the next algorithm :

现在在 jQuery (javascript) 我做这样的事情: 1st) 我声明一个全局变量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 */

At the line of : $.notify("Please refresh "+event.data, "info");is there that you can handle the message.

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

For my case i used to send an jQuery notify.

对于我的情况,我曾经发送一个 jQuery 通知。

You may use POSIX PIPES or a DB Table instead to pass the "message" via POST since the sseServer.php does something like an "infinite loop".

您可以使用 POSIX PIPES 或数据库表来通过 POST 传递“消息”,因为 sseServer.php 会执行类似于“无限循环”的操作。

My problem at the time is that the above code DOES NOT SENDS THE "message" to all clients but only to the pair (client that called the sseServer.php works as individual to every pair) so i'll change the technik and to a DB update from the page that i want to trigger the "message" and then the sseServer.php instead to get the message via POST it will get it from DB table.

我当时的问题是,上面的代码不会将“消息”发送给所有客户端,而是只发送给一对(调用 sseServer.php 的客户端对每一对来说都是单独的),所以我会改变技术和从我想触发“消息”的页面更新数据库,然后 sseServer.php 而不是通过 POST 获取消息,它将从数据库表中获取它。

I hope that i have help!

我希望我有帮助!

回答by Colin M

This is really a structural question about your application. Real-time events are something that you want to think about from the beginning, so you can design your application around it. If you have written an application that just runs a bunch of random mysql(i)_querymethods using string queries and doesn't pass them through any sort of intermediary, then many times you won't have a choice but to either rewrite much of your application, or do constant server-side polling.

这实际上是关于您的应用程序的结构性问题。实时事件是您想要从一开始就考虑的事情,因此您可以围绕它设计您的应用程序。如果您编写的应用程序只mysql(i)_query使用字符串查询运行一堆随机方法并且不通过任何类型的中介传递它们,那么很多时候您将别无选择,只能重写大部分应用程序,或者这样做持续的服务器端轮询。

If, however, you manage your entities as objects and pass them through some sort of intermediary class, you can hook into that process. Look at this example:

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

<?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
    }
}

In your application, when you're ready to save:

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

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

This is not the most graceful example but it should serve as a decent building block. You can hook into your actual persistence layer to handle triggering these events. Then you get them immediately (as real-time as it can get) without hammering your server (since you have no need to constantly query your database and see if things changed).

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

You obviously won't catch manual changes to the database this way - but if you're doing anything manually to your database with any frequency, you should either:

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

  • Fix the problem that requires you to have to make a manual change
  • Build a tool to expedite the process, and fire these events
  • 修复需要您手动更改的问题
  • 构建一个工具来加快进程,并触发这些事件

回答by Prosto Trader

Basically, PHP is not suitable techonology for this sort of things. Yes you can make it work, but it will be a disaster on highload. We run stockservers that send stock-change signals via websockets to dozens thousends users - and If we'd use php for that... Well, we could, but those homemade cycles - is just a nightmare. Every single connection will make a separate process on server or you have to handle connections from some sort of database.

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

Simply use nodejs and socket.io. It will let you easily start and have a running server in couple days. Nodejs has own limitations also, but for websockets (and SSE) connections now its the most powerfull technology.

只需使用 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.

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