Широковещательные сообщения с событиями Server-Sent

Я хочу реализовать простое приложение событий 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 всем клиентам.

Это возможно? Есть ли другой маршрут, по которому я могу пойти?

заранее спасибо


person kbariotis    schedule 07.01.2013    source источник


Ответы (4)


PHP для этого не подходит. Если у вас много клиентов, ожидающих этого события, вам потребуется экземпляр интерпретатора PHP (и, возможно, поток apache) для каждого ожидающего клиента.

Что вы могли бы подумать о том, чтобы обработать это на сервере, управляемом событиями. Node.JS и Tornado (Python) — это две среды, которые я использовал в прошлом в аналогичных обстоятельствах. Если вы используете Node.js, вы можете заглянуть в socket.io для этого — он не использует события, отправленные сервером, но сделает то, что вы хотите, очень простым.

У вас может быть два веб-сервера рядом: ваш текущий сервер (я предполагаю, что это Apache) и один, написанный вами для обработки клиентов, ожидающих обновлений. Всякий раз, когда сервер PHP хочет отправить сообщение ожидающим клиентам, сервер PHP уведомляет другой сервер (AMQP, ZeroMQ, redis Pub/Sub или простой HTTP-запрос на непубличном интерфейсе — все это было бы хорошим выбором), и этот сервер передает сообщение всем ожидающим клиентам.

Вы можете увидеть очень простую версию того, о чем я говорю, используя node.js + socket.io (который можно легко заменить SSE) на гитхаб.

person JeffS    schedule 08.01.2013
comment
Если люди заинтересованы, я мог бы реализовать реализацию, подобную той, о которой я говорил, где-то позже на этой неделе. - person JeffS; 09.01.2013
comment
мне было бы интересно, хотя я сейчас пытаюсь это реализовать. Так что не утруждайте себя потерей времени, если вы действительно этого не хотите. :) Спасибо - person kbariotis; 09.01.2013
comment
замечательно, а также получил точку. Отличная реализация! Также нашел это, что является еще одной отличной реализацией, использующей также PHP для сервера и Coffeescript для клиента. Большое спасибо @JeffS - person kbariotis; 09.01.2013
comment
Есть ли способ сделать многоадресную рассылку с использованием событий, отправленных сервером? - person kapil das; 30.11.2017
comment
Нет, нет никакого способа сделать настоящий мультикаст sse. Для Sse требуется одно TCP-подключение к серверу для каждого клиента. Очевидно, что вы можете имитировать многоадресную рассылку, имея простую настройку сервера, которая отправляет одни и те же сообщения всем подключенным клиентам. - person JeffS; 02.12.2017

важное РЕДАКТИРОВАТЬ: (через день)

после тестирования я обнаружил, что это очень плохое решение.

прецедент:

  • клиенты: запустить 8 клиентов (в одном браузере(Chromium,(k)ubuntu), одна вкладка, 8 iframe).
  • API сервера: обработчик Apache 2.0
  • Версия PHP 5.3.10-1ubuntu3.4

результат:

Открыто 6 SSE-соединений. но 2 SSE-соединения остались незавершенными. другие страницы на сервере не могут быть открыты до закрытия SSE-клиентов.

но сервер может обрабатывать много клиентов с помощью ajax (повторять ajax каждую 1 секунду).


Старый ответ:

использовать общую память.

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 раза в секунду, то сохраняется только последнее сообщение)

person seyed    schedule 08.01.2013
comment
Меня интересует этот вопрос. и будут +1 лучшие решения. :D - person seyed; 09.01.2013
comment
Результаты вашего тестирования почти наверняка влияют на то, как apache обрабатывает соединения, по крайней мере, с одним потоком на соединение, потоки слишком тяжелы для этого. Вот почему альтернативное решение, которое я предложил, фактически избегает использования apache (и PHP) для части, которая будет иметь много длинных открытых подключений к ней. - person JeffS; 11.01.2013
comment
Вы также ограничены браузером в количестве открытых сокетов на домен. Вы можете вспомнить проблему шардинга домена для активов. То же самое. - person oif_vet; 28.01.2014

Что касается ограничения количества, это связано с тем, что браузер ограничивает количество подключений к одному и тому же серверу, вы можете изменить это путем перенастройки. В firefox такой же номер по умолчанию (6).

Кроме того, мне интересно, как обрабатывать событие отключения на стороне сервера, если слишком много клиентов используют SSE, необходимо управлять соединениями, даже когда используются другие ресурсы, такие как сокеты для других служб.

person bwlee    schedule 19.08.2013

Вся страница не получит одно и то же сообщение одновременно.

Они получат сообщение только после подключения.

Если вы хотите, вы можете изучить WebSockets и/или Node.JS, которые могут обеспечить более «мгновенный» обмен сообщениями.

person Naftali aka Neal    schedule 07.01.2013
comment
По сути, я хотел бы иметь файл (или структуру) для прослушивания запросов POST, редактирования их и отправки данных всем подключенным клиентам, которые прослушивают события сервера. Как бы вы порекомендовали это реализовать? - person kbariotis; 08.01.2013
comment
SSE так же мгновенна, как WebSockets, в общении между сервером и клиентом. На самом деле может быть даже немного быстрее из-за меньших накладных расходов и более быстрого рукопожатия. - person Kornel; 09.01.2013