每个应用程序应该只有一个 EventSource 对象吗?

Posted

技术标签:

【中文标题】每个应用程序应该只有一个 EventSource 对象吗?【英文标题】:Should there be only one EventSource object per app? 【发布时间】:2012-07-25 05:04:34 【问题描述】:

当使用Server-Sent Events时,客户端应该建立多个连接来接收它感兴趣的不同事件,还是应该只有一个连接并且客户端通过单独的通道指示它感兴趣的内容? IMO 后者似乎更可取,尽管对某些人来说它可能会使客户端代码更复杂。该规范支持命名事件(与特定主题相关的事件),这对我来说建议服务器发送事件连接应该用作所有事件的单一通道。

以下代码说明了启动多个 Server-Sent Event 连接的第一种情况:

var EventSource eventSource1 = new EventSource("events/topic1");
eventSource1.addEventListener('topic1', topic1Listener, false);

var EventSource eventSource2 = new EventSource("events/topic2");
eventSource2.addEventListener('topic2', topic2Listener, false);

eventSource1 将接收“topic1”事件,而 eventSource2 将接收“topic2”事件。虽然这非常简单,但对于您感兴趣的每个主题都会发生挂起的 GET 也非常低效。

替代方法如下:

var EventSource eventSource3 = new EventSource("/events?id=1234")
eventSource3.addEventListener('topic3', topic3Listener, false);
eventSource3.addEventListener('topic4', topic4Listener, false);

var subscription = new XMLHttpRequest();
subscription.open("PUT", "/events/topic3?id=1234", true);
subscription.send();

在此示例中,将存在单个 EventSource,并且对特定事件的兴趣将由具有服务器发送事件连接的单独请求指定,并且注册由 id 参数关联。 topic3Listener 会收到“topic3”事件,而 topic4Listener 不会。虽然需要更多的代码,但好处是只建立了一个连接,但仍然可以以不同的方式识别和处理事件。

网络上有许多示例显示了命名事件的使用,但似乎事件名称(或主题)是预先知道的,因此客户端无需向服务器注册兴趣(@987654322 @)。虽然我还没有看到显示多个 EventSource 对象的示例,但我也没有看到显示客户端使用单独的请求来注册对特定主题的兴趣的示例,就像我在上面所做的那样。我对规范的解释使我相信,表明对某个主题(或事件名称)的兴趣完全取决于开发人员,并且可以在客户知道将要接收的事件名称的情况下静态完成或客户端动态地提醒服务器它有兴趣接收特定事件。

我很想听听其他人对该主题的看法。注意:我通常是 Java 开发人员,所以请原谅我平庸的 JS 代码.. :)

【问题讨论】:

您可能不会在事件流中使用所有事件名称,而是可以监听“消息”事件,并且在 event.data 中您可以编码您的 topicId 和其他信息 是的,但是这意味着我需要在有效负载中编码此类数据,当消息识别已经是规范数据框架的一部分时,这实际上没有意义。 +1 !很好的问题。你最后做了什么?我也在考虑采用第二种方法。如果您分享您是否遇到任何问题,将不胜感激。 @JamesTyrrell 您选择了哪种解决方案?您能否用几句话分享您的经验(或者在 medium.com 上的文章中更好:))? 【参考方案1】:

恕我直言,我强烈建议您为每个提供 SSE 的服务拥有一个 EventSource 对象,然后使用不同类型发出消息。

不过,最终这取决于消息类型的相似程度。例如,如果您有 5 种不同类型的与用户相关的消息,则有一个用户 EventSource 并区分事件类型。

如果您有一种关于用户的事件类型,另一种关于三明治的事件类型,我会说将它们放在不同的服务中,因此EventSources。

最好将 EventSources 分解为与宁静服务相同的方式。如果您不能使用 AJAX 从同一个服务中获取两个东西,那么您可能不应该从同一个 EventSource 中获取它们。

【讨论】:

这可以扩展吗? 从架构/结构的角度来看,我同意(出于您提到的所有原因),但存在与连接限制相关的成本:您要么支持 HTTP/2(包括强制性端到端end TLS)或解决方法,例如为每个 SSE 服务设置一个子域。由于各种原因,两者都可能很麻烦,因此需要权衡取舍。【参考方案2】:

为了应对模糊和宽松的浏览器标准解释*,浏览器供应商对允许到单个域/端口的持久连接数实施不一致的限制。由于异步上下文的每个事件接收器都假定只要该接收器处于打开状态,就会分配一个持久连接,因此严格限制 EventSource 侦听器的数量以避免超出不同的供应商特定限制是至关重要的。实际上,这将您限制为每个应用程序大约 6 个 EventSource/async 上下文对。降级是正常的(例如,额外的 EventSource 连接请求只会等到插槽可用),但请记住,必须有可用于检索页面资源、响应 XHR 等的连接。

*W3C 已经发布了关于持久连接的标准,其中包含“……应该限制同时连接的数量……”这种语言意味着该标准不是强制性的,因此供应商的合规性是可变的。 http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4

【讨论】:

以上是关于每个应用程序应该只有一个 EventSource 对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

Azure SDK 2.6 中缺少 ETW EventSource 表

EventSource 与轮询 ajax 的效率/开销?

依赖关系在大型应用程序中为语义日志实现 EventSource

用户注销时无法取消订阅 eventSource

所有事件的 HTML5 EventSource 侦听器?

从(服务器发送的)EventSource 创建一个 RxJS Observable