Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Broadcasting messages with Server-Sent events

I want to implement a simple Server-Sent events app where clients listen to a php file like this

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

but then i need to be able to broadcast a message and every connected client gets it simultaneously.

The php file will be triggered by another request and it will "echo" some SSE data to all clients.

Is this possible? Is there another route i can take?

Thank you in advance

like image 925
kbariotis Avatar asked Jan 07 '13 18:01

kbariotis


2 Answers

PHP is not going to be great for this. If you have lots of clients waiting for this event, you're going to require an instance of the PHP interpreter (and probably an apache thread) for each client that's waiting.

What you might consider doing is having an event driven server handle this. Node.JS and Tornado (Python) are two environments I've used in the past in similar circumstances. If you use Node.js, you may want to look into socket.io for this -- it doesn't use Server Sent Events, but would make what you want very simple.

You could have two webservers side by side, your current server (I'm assuming Apache) and one written by you to handle the clients waiting for updates. Whenever the PHP server wants to send a message to waiting clients, the PHP server notifies the other server (AMQP, ZeroMQ, redis Pub/Sub or a simple HTTP request on a non-public interface would all be good choices), and that server passes on the message to any waiting clients.

You can see a very simple version of what I'm talking about using node.js + socket.io (Which could easily be replaced by SSE) on github.

like image 52
JeffS Avatar answered Nov 15 '22 10:11

JeffS


important EDIT: (after a day)

after testing i found this is so bad solution.

test case:

  • clients: run 8 clients (in one browser(Chromium,(k)ubuntu), one tab, 8 iframe).
  • Server API: Apache 2.0 Handler
  • PHP Version 5.3.10-1ubuntu3.4

result:

6 SSE-connections opened. but 2 SSE-connections left pending. other pages in server cannot be open until close SSE-clients.

but server can handle many clients by ajax (repeat ajax every 1 second).


Old Answer:

use share memory.

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

disadvantage of this way:

  1. one second delay
  2. long time open connections. and this cause non-Scalability.

in my implementation sse.php send only last message in a second. but you can improve it and use database. (if trigger.php called 3 times in a second, then only last message saved)

like image 31
seyed Avatar answered Nov 15 '22 09:11

seyed