Подтвердить что ты не робот

Как действительно серверные события действительно работают?

Итак, я понимаю концепцию событий, отправленных сервером (EventSource):

  • Клиент подключается к конечной точке через EventSource
  • Клиент просто прослушивает сообщения, отправленные с конечной точки.

То, что я запутался, - это то, как это работает на сервере. Я рассмотрел разные примеры, но тот, который приходит на ум, - это Mozilla's: http://hacks.mozilla.org/2011/06/a-wall-powered-by-eventsource-and-server-sent-events/

Теперь это может быть просто плохим примером, но, как я понимаю, это имеет смысл, как работает серверная сторона:

  • Что-то меняется в хранилище данных, например в базе данных
  • Серверная сторона script опросает хранилище данных каждую N-ю секунду
  • Если опрос script отмечает изменение, серверное событие отправляется клиентам

Это имеет смысл? Действительно ли это работает с точки зрения barebone?

4b9b3361

Ответ 1

Сайт доктора HTML5 имеет отличную запись на событиях, отправленных сервером, но я постараюсь предоставить (разумно) короткий здесь также.

События, отправленные сервером, по сути являются длинным HTTP-соединением, специальным типом mime (text/event-stream) и пользовательским агентом, который предоставляет API EventSource. Вместе они создают основу однонаправленной связи между сервером и клиентом, где сообщения могут отправляться с сервера на клиент.

На стороне сервера это довольно просто. Все, что вам действительно нужно сделать, это установить следующие заголовки http:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

Обязательно ответьте кодом 200, а не 204 или любым другим кодом, так как это приведет к отключению совместимых пользовательских агентов. Кроме того, не завершайте соединение на стороне сервера. Теперь вы можете начать отталкивать сообщения по этому соединению. В nodejs (с помощью выражения) это может выглядеть примерно так:

app.get("/my-stream", function(req, res) {
    res.status(200)
       .set({ "content-type"  : "text/event-stream"
            , "cache-control" : "no-cache"
            , "connection"    : "keep-alive"
            })

    res.write("data: Hello, world!\n\n")
})

На клиенте вы просто используете API EventSource, как вы отметили:

var source = new EventSource("/my-stream")
source.addEventListener("message", function(message) {
    console.log(message.data)
})

И что это, в основном.

Теперь, на практике, на самом деле происходит то, что соединение поддерживается сервером и клиентом посредством взаимного договора. Сервер будет поддерживать соединение до тех пор, пока он сочтет нужным. Если он захочет, он может завершить соединение и ответить с помощью 204 No Content в следующий раз, когда клиент попытается подключиться. Это заставит клиента перестать пытаться снова подключиться. Я не уверен, есть ли способ завершить соединение таким образом, что клиенту будет предложено не пересоединяться вообще, тем самым пропуская клиент, пытающийся повторно подключиться.

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

Один очень важный бит, который я до сих пор едва затронул, это тип mime. Тип mime определяет формат сообщения, идущего вниз по соединению. Обратите внимание, однако, что это не диктует формат содержимого сообщений, а только структуру самих сообщений. Тип мим очень прост. Сообщения - это, по сути, пара ключей/значений информации. Ключ должен быть одним из предопределенных множеств:

  • id - идентификатор сообщения
  • данные - фактические данные
  • событие - тип события
  • retry - миллисекунды, которые должен ожидать пользовательский агент перед повторной попыткой неудачного подключения.

Любые другие клавиши следует игнорировать. Сообщения затем разделяются с использованием двух символов новой строки: \n\n

Ниже приведено правильное сообщение: (последние новые символы строки добавлены для подробностей)

data: Hello, world!
\n

Клиент увидит это как: Hello, world!.

Как это:

data: Hello,
data: world!
\n

Клиент увидит это как: Hello,\nworld!.

Это в значительной степени суммирует, какие события, отправленные сервером: долгое не кэшированное http-соединение, тип mime и простой javascript API.

Для получения дополнительной информации я настоятельно рекомендую прочитать спецификацию . Он очень мал и очень хорошо описывает вещи (хотя требования серверной части можно свести к лучшему.) Я настоятельно рекомендую прочитать его для ожидаемого поведения с определенными кодами статуса http, например.