6

我想实现一个简单的服务器发送事件应用程序,其中客户端侦听这样的 php 文件

<script src="eventsource.js"></script>
<script>
  var es = new EventSource('../sse.php');

  es.onmessage = function (event) {
    var data = JSON.parse(event.data);
    console.log('msg: ' + event.data);
  };

  es.onopen = function () {
    console.log('connected');
  }
</script>

但是然后我需要能够广播一条消息,并且每个连接的客户端都会同时收到它。

php 文件将由另一个请求触发,它将向所有客户端“回显”一些 SSE 数据。

这可能吗?我可以采取其他路线吗?

先感谢您

4

4 回答 4

7

PHP 在这方面不会很好。如果您有很多客户端在等待此事件,那么您将需要为每个等待的客户端提供一个 PHP 解释器实例(可能还有一个 apache 线程)。

您可能会考虑让事件驱动的服务器处理此问题。Node.JS 和 Tornado (Python) 是我过去在类似情况下使用过的两种环境。如果您使用 Node.js,您可能需要查看 socket.io ——它不使用服务器发送事件,但会让您想要的非常简单。

您可以并排拥有两个网络服务器,您当前的服务器(我假设是 Apache)和一个由您编写的用于处理等待更新的客户端。每当 PHP 服务器想要向等待的客户端发送消息时,PHP 服务器会通知另一台服务器(AMQP、ZeroMQ、redis Pub/Sub 或非公共接口上的简单 HTTP 请求都是不错的选择),然后该服务器将消息传递给任何等待的客户端。

您可以在github上看到我正在谈论的使用 node.js + socket.io(可以很容易地被 SSE 替换)的一个非常简单的版本。

于 2013-01-08T21:07:29.050 回答
1

重要编辑:(一天后)

经过测试,我发现这是一个非常糟糕的解决方案。

测试用例:

  • 客户端:运行 8 个客户端(在一个浏览器中(Chromium,(k)ubuntu),一个选项卡,8 个 iframe)。
  • 服务器 API:Apache 2.0 处理程序
  • PHP 版本 5.3.10-1ubuntu3.4

结果:

6 个 SSE 连接打开。但 2 个 SSE 连接未决。在关闭 SSE 客户端之前,服务器中的其他页面无法打开。

但是服务器可以通过 ajax 处理许多客户端(每 1 秒重复一次 ajax)。


老答案:

使用共享内存。

sse.php

<?php

header("Content-Type: text/event-stream\n\n");
header('Cache-Control: no-cache');

$key = "987654";
$permissions = 0666;
$size = 1024;

//Create or open the shared memory segment.
$segment = shm_attach($key, $size, $permissions);

$msg = ' null ';
$last_time = 0;
$time = 0;
while (1) {

    if (shm_has_var($segment, 0)) {
        $time = shm_get_var($segment, 0);
    }
    if (shm_has_var($segment, 0)) {
        $msg = shm_get_var($segment, 1);
    }
    $now = time();
    if ($last_time < $time) {
        $last_time = $time;
        echo 'data: msg(' . $msg . ') time(' . $time . ')';
        echo "\n\n";
    }

    ob_flush();
    flush();
    sleep(1);
}

触发器.php

<?php 
if (isset($_GET['msg'])) {

    $key = "987654";// TODO: how to generate suitable key?
    $permissions = 0666;
    $size = 1024;

    $segment = shm_attach($key, $size, $permissions);

    $time = time();
    $msg = $_GET['msg'];

    shm_put_var($segment, 0, $time);
    shm_put_var($segment, 1, $msg);
    echo $time;
}

这种方式的缺点:

  1. 一秒延迟
  2. 长时间打开连接。这会导致不可扩展性。

在我的实现中,sse.php 在一秒钟内只发送最后一条消息。但你可以改进它并使用数据库。(如果 trigger.php 在一秒钟内调用了 3 次,则只保存最后一条消息)

于 2013-01-08T20:47:09.363 回答
0

对于数量限制,这是因为浏览器限制到同一服务器的连接数,您可以通过重新配置来更改。Firefox 中有相同的默认编号 (6)。

另外,我想知道如何处理服务器端的断开连接事件,如果太多的客户端使用 SSE,则有必要管理连接,即使使用其他资源(例如连接到其他服务的套接字)也是如此。

于 2013-08-19T23:13:44.637 回答
-1

所有页面不会同时收到相同的消息。

他们只有在连接后才会收到消息。

如果您愿意,您可以查看 WebSockets 和/或 Node.JS,它们可以允许更多“即时”消息传递。

于 2013-01-07T19:22:20.497 回答