使用Server-Sent事件广播消息

时间:2013-01-07 18:52:27

标签: php html5 sockets broadcast server-sent-events

我想实现一个简单的Server-Sent事件应用程序,其中客户端监听像这样的

的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 个答案:

答案 0 :(得分:7)

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

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

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

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

答案 1 :(得分:1)

重要编辑:(一天后)

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

测试用例:

  • 客户:运行8个客户端(在一个浏览器中(Chromium,(k)ubuntu),一个选项卡,8个iframe)。
  • 服务器API:Apache 2.0处理程序
  • PHP Version 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);
}

trigger.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. 长时间打开连接。这导致了不可扩展性。
  3. 在我的实现中,sse.php只在一秒内发送最后一条消息。但你可以改进它并使用数据库。 (如果trigger.php在一秒钟内调用3次,则只保存最后一条消息)

答案 2 :(得分:0)

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

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

答案 3 :(得分:-1)

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

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

如果需要,您可以查看WebSockets和/或Node.JS,它们可以提供更多“即时”消息传递。