服务器发送的事件如何实际工作?

Ahm*_*man 41 server-sent-events

所以我理解服务器发送事件的概念(EventSource):

  • 客户端通过端口连接到端点 EventSource
  • 客户端只监听从端点发送的消息

我很困惑的是它在服务器上是如何工作的.我看了一下不同的例子,但想到的一个是Mozilla的:http://hacks.mozilla.org/2011/06/a-wall-powered-by-eventsource-and-server-sent-事件/

现在这可能只是一个不好的例子,但是根据我的理解,服务器端如何工作有点有道理:

  • 数据存储区中的某些更改,例如数据库
  • 服务器端脚本每隔N秒轮询数据存储区
  • 如果轮询脚本注意到更改,则会向客户端触发服务器发送的事件

那有意义吗?从准确的角度来看,它真的是如何运作的吗?

Mar*_*ade 56

HTML5医生网站对服务器发送的事件进行了很好的记录,但我也试着在这里提供一个(合理的)简短摘要.

服务器发送的事件的核心是长连接的http连接,一个特殊的mime type(text/event-stream)和一个提供EventSourceAPI 的用户代理.它们共同构成了服务器和客户端之间单向连接的基础,消息可以从服务器发送到客户端.

在服务器端,它相当简单.您真正需要做的就是设置以下http标头:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Run Code Online (Sandbox Code Playgroud)

请务必使用代码200而不是代码204或任何其他代码进行响应,因为这会导致兼容的用户代理断开连接.另外,请确保不要在服务器端结束连接.您现在可以自由地开始按下该连接.在nodejs(使用express)中,这可能类似于以下内容:

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")
})
Run Code Online (Sandbox Code Playgroud)

在客户端,您只需使用EventSourceAPI,如您所述:

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

就是这样,基本上就是这样.

现在,实际上,这里实际发生的是服务器和客户端通过相互契约保持连接.只要认为合适,服务器就会保持连接的活动状态.如果需要,它可以终止连接并在204 No Content下次客户端尝试连接时进行响应.这将导致客户端停止尝试重新连接.我不确定是否有办法以客户端被告知不重新连接的方式结束连接,从而跳过尝试重新连接一次的客户端.

如上所述,客户端也将保持连接活动,并尝试重新连接,如果它被删除.重新连接的算法在规范中指定,并且非常简单.

然而,我到目前为止几乎没有触及的一个非常重要的部分是mime类型.mime类型定义了连接下来的消息格式.但请注意,它并没有规定消息内容的格式,只是消息本身的结构.哑剧类型非常直接.消息本质上是键/值对信息.密钥必须是预定义集之一:

  • id - 消息的ID
  • 数据 - 实际数据
  • event - 事件类型
  • retry - 用户代理在重试失败的连接之前应等待的milleseconds

应忽略任何其他键.然后通过使用两个换行符来分隔消息:\n\n

以下是有效消息:(为了详细程度而添加的最后一个新行字符)

data: Hello, world!
\n
Run Code Online (Sandbox Code Playgroud)

客户会将此视为:Hello, world!.

就是这样:

data: Hello,
data: world!
\n
Run Code Online (Sandbox Code Playgroud)

客户会将此视为:Hello,\nworld!.

这几乎总结了服务器发送的事件:长时间运行的非缓存http连接,mime类型和简单的JavaScript API.

有关更多信息,我强烈建议您阅读规范.它很小并且描述的东西非常好(虽然服务器端的要求可能会更好地总结.)我强烈建议阅读它以获得某些http状态代码的预期行为.

  • 这是伟大的关于客户端,以及如何连接的工作原理的细节,但如果事件需要一个数据库更改发送我认为是没有选择,只能不断查询服务器上的数据库?我认为这是op所要求的?是否有更高效的服务器端解决方案 - 显然,如果有循环不断检查数据库更改,那么小型企业服务器上的许多连接就不会开始放慢速度. (3认同)
  • 重试:毫秒位 - 天才!救了我的皮肤! (3认同)
  • 每当服务器决定发送一条消息时,客户端只会收到一条消息,它从不轮询任何内容。服务器可以自行确定何时以及为何发送消息。这可以使用轮询或任何符合要求的技术在服务器端实现。可以汇集服务器上的连接,并且可以向每个连接发送一条消息,为您提供一种广播功能。因此,服务器可以是到数据库的单个连接,但可以广播到连接到服务器的大量客户端。我认为这个问题比这更笼统。 (2认同)

vka*_*v15 6

您还需要确保调用res.flushHeaders(),否则 Node.js 在您调用之前不会发送 HTTP 标头res.end()。请参阅本教程以获取完整示例。