简体   繁体   English

服务器发送事件和浏览器限制

[英]Server sent events and browser limits

I have a web application that listens for Server Sent Events.我有一个监听服务器发送事件的 web 应用程序。 While I was working and testing with multiple windows open, things were not working and I banged my head for several times looking in the wrong direction: eventually, I realized that the problem was concurrent connections.当我在打开多个 windows 的情况下工作和测试时,一切都没有工作,我朝错误的方向撞了好几次头:最终,我意识到问题出在并发连接上。

However I was testing a very limited number and even if I am running the test on Apache (I know, I should use node).但是我测试的数量非常有限,即使我在 Apache 上运行测试(我知道,我应该使用节点)。

I then, switched browser and noticed something really interesting: apparently Chrome limits Server Sent Events connections to 4-5, while Opera doesn't.然后,我切换浏览器并注意到一些非常有趣的事情:显然 Chrome 将服务器发送事件连接限制为 4-5,而 Opera 则没有。 Firefox, on the other hand, after 4-5 simultaneous connections, refuses to load any other page.另一方面,Firefox 在 4-5 个同时连接后,拒绝加载任何其他页面。

What is the reason behind this?这背后的原因是什么? Does the limit only apply to SSE connections from the same source, or would it be the same if I were to test open them from a different domain?该限制是否仅适用于来自同一来源的 SSE 连接,或者如果我要测试从不同域打开它们是否相同? Is there any chance that I am misusing SSE and this is actually blocking the browsers, or this is a known behaviour?我是否有可能滥用 SSE 而这实际上阻止了浏览器,或者这是一种已知行为? Is there any way around it?有什么办法吗?

The way this works in all browsers are that each domain gets a limited amount of connections and the limits are global for your whole application.这在所有浏览器中的工作方式是每个域获得有限数量的连接,并且限制对您的整个应用程序是全局的。 That means if you have one connection open for realtime communication you have one less for loading images, CSS and other pages.这意味着,如果您为实时通信打开了一个连接,则用于加载图像、CSS 和其他页面的连接就会减少。 On top of that you don't get new connections for new tabs or windows, all of them needs to share the same amount of connections.最重要的是,您不会为新选项卡或 windows 获得新连接,它们都需要共享相同数量的连接。 This is very frustrating but there are good reasons for limiting the connections.这非常令人沮丧,但有充分的理由限制连接。 A few years back, this limit was 2 in all browsers (based on the rules in ( http://www.ietf.org/rfc/rfc2616.txt ) HTTP1.1 spec) but now most browsers use 4-10 connections in general.几年前,这个限制在所有浏览器中都是 2(基于 ( http://www.ietf.org/rfc/rfc2616.txt ) HTTP1.1 规范中的规则),但现在大多数浏览器使用 4-10 个连接一般的。 Mobile browsers on the other hand still needs to limit the amount of connections for battery saving purposes.另一方面,移动浏览器仍需要限制连接数量以节省电池电量。

These tricks are available:这些技巧可用:

  1. Use more host names.使用更多的主机名。 By assigning ex.通过分配 ex。 www1.example.com , www2.example.com you get new connections for each host name. www1.example.com , www2.example.com您会为每个主机名获得新的连接。 This trick works in all browsers.这个技巧适用于所有浏览器。 Don't forget to change the cookie domain to include the whole domain ( example.com , not www.example.com )不要忘记更改 cookie 域以包含整个域( example.com ,而不是www.example.com
  2. Use web sockets.使用 web sockets。 Web sockets are not limited by these restrictions and more importantly they are not competing with the rest of your websites content. Web sockets 不受这些限制,更重要的是,它们不会与您网站内容的 rest 竞争。
  3. Reuse the same connection when you open new tabs/windows.打开新选项卡/窗口时重复使用相同的连接。 If you have gathered all realtime communication logic to an object call Hub you can recall that object on all opened windows like this:如果您已将所有实时通信逻辑收集到 object 呼叫集线器,您可以回忆起所有打开的 windows 上的 object,如下所示:

window.hub = window.opener? window.opener.hub || new Hub() window.hub = window.opener? window.opener.hub || new Hub() 4. or use flash - not quite the best advice these days but it might still be an option if websockets aren't an option. window.hub = window.opener? window.opener.hub || new Hub() 4. 或使用 flash - 这些天不是最好的建议,但如果 websockets 不是一个选项,它可能仍然是一个选项。 5. Remember to add a few seconds of time between each SSE request to let queued requests to be cleared before starting a new one. 5. 请记住在每个 SSE 请求之间添加几秒钟的时间,以便在开始新的请求之前清除排队的请求。 Also add a little more waiting time for each second the user is inactive, that way you can concentrate your server resources on those users that are active.还要为用户处于非活动状态的每一秒增加一点等待时间,这样您就可以将服务器资源集中在那些处于活动状态的用户上。 Also add a random number of delay to avoid the Thundering Herd Problem还添加一个随机数的延迟以避免雷霆群问题

Another thing to remember when using a multithreaded and blocking language such as Java or C# you risk using resources in your long polling request that are needed for the rest of your application.使用多线程和阻塞语言(例如 Java 或 C#)时要记住的另一件事是,您可能会在长轮询请求中使用应用程序的 rest 所需的资源。 For example in C# each request locks the Session object which means that the whole application is unresponsive during the time a SSE request is active.例如,在 C# 中,每个请求都会锁定 Session object,这意味着在 SSE 请求处于活动状态期间整个应用程序没有响应。

NodeJs is great for these things for many reasons as you have already figured out and if you were using NodeJS you would have used socket.io or engine.io that takes care of all these problems for you by using websockets, flashsockets and XHR-polling and also because it is non blocking and single threaded which means it will consume very little resources on the server when it is waiting for things to send. NodeJs 非常适合这些事情,原因有很多,因为您已经知道了,如果您使用 NodeJS,您将使用 socket.io 或 engine.io 通过使用 websockets、flashsockets 和 XHR-polling 为您解决所有这些问题因为它是非阻塞和单线程的,这意味着它在等待发送内容时将在服务器上消耗很少的资源。 A C# application consumes one thread per waiting request which takes at least 2MB of memory just for the thread. C# 应用程序在每个等待请求中消耗一个线程,这至少需要 2MB 的 memory 仅用于线程。

One way to get around this issue is to shut down the connections on all the hidden tabs, and reconnect when the user visits a hidden tab.解决此问题的一种方法是关闭所有隐藏选项卡上的连接,并在用户访问隐藏选项卡时重新连接。

I'm working with an application that uniquely identifies users which allowed me to implement this simple work-around:我正在使用一个唯一标识用户的应用程序,它允许我实现这个简单的解决方法:

  1. When users connect to sse, store their identifier, along with a timestamp of when their tab loaded.当用户连接到 sse 时,存储他们的标识符,以及他们的选项卡加载时间的时间戳。 If you are not currently identifying users in your app, consider using sessions & cookies.如果您当前没有在您的应用程序中识别用户,请考虑使用会话和 cookies。
  2. When a new tab opens and connects to sse, in your server-side code, send a message to all other connections associated with that identifier (that do not have the current timestamp) telling the front-end to close down the EventSource.当一个新选项卡打开并连接到 sse 时,在您的服务器端代码中,向与该标识符关联的所有其他连接(没有当前时间戳)发送一条消息,告诉前端关闭 EventSource。 The front-end handler would look something like this:前端处理程序看起来像这样:

    myEventSourceObject.addEventListener('close', () => { myEventSourceObject.close(); myEventSourceObject = null; });

  3. Use the javascript page visibility api to check to see if an old tab is visible again, and re-connect that tab to the sse if it is.使用 javascript 页面可见性 api 检查旧选项卡是否再次可见,如果是,则将该选项卡重新连接到 sse。

    document.addEventListener('visibilitychange', () => { if (.document;hidden && myEventSourceObject === null) { // reconnect your eventsource here } })

  4. If you set up your server code like step 2 describes, on re-connect, the server-side code will remove all the other connections to the sse.如果您按照步骤 2 所述设置服务器代码,则在重新连接时,服务器端代码将删除与 sse 的所有其他连接。 Hence, you can click between your tabs and the EventSource for each tab will only be connected when you are viewing the page.因此,您可以在您的选项卡之间单击,并且每个选项卡的 EventSource 将仅在您查看页面时连接。

Note that the page visibility api isn't available on some legacy browsers: https://caniuse.com/#feat=pagevisibility请注意,页面可见性 api 在某些旧版浏览器上不可用: https://caniuse.com/#feat=pagevisibility

You are right about the number of simultaneous connections.您对同时连接的数量是正确的。

You can check this list for max values: http://www.browserscope.org/?category=network您可以查看此列表的最大值: http://www.browserscope.org/?category=network

And unfortunately, I never found any work around, except multiplexing and/or using different hostnames.不幸的是,除了多路复用和/或使用不同的主机名之外,我从未找到任何解决方法。

2022 Update 2022 更新

This problem has been fixed in HTTP/2.此问题已在 HTTP/2 中修复。 According to mozilla docs :- When not used over HTTP/2, SSE suffers from a limitation to the maximum number of open connections, which can be especially painful when opening multiple tabs, as the limit is per browser and is set to a very low number (6).根据mozilla 文档:- 当不通过 HTTP/2 使用时,SSE 会受到最大打开连接数的限制,这在打开多个选项卡时尤其痛苦,因为限制是每个浏览器的并且设置为非常低号 (6)。

The issue has been marked as "Won't fix" in Chrome and Firefox.该问题已在 Chrome 和 Firefox 中标记为“无法修复”。 This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to www.1.example and another 6 SSE connections to www.2.example (per Stackoverflow).此限制针对每个浏览器 + 域,这意味着您可以在所有选项卡上打开 6 个 SSE 连接到www.1.example ,另外 6 个 SSE 连接到www.2.example (每个 Stackoverflow)。

When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100).使用 HTTP/2 时,服务器和客户端之间协商同时的 HTTP 流的最大数量(默认为 100)。

Spring Boot 2.1+ ships by default with Tomcat 9.0.x which supports HTTP/2 out of the box when using JDK 9 or later. Spring Boot 2.1+默认附带 Tomcat 9.0.x,它在使用 JDK 9 或更高版本时支持开箱即用的 HTTP/2。

If you are using any other backend, please enable http/2 to fix this issue.如果您使用任何其他后端,请启用 http/2 来解决此问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM