【问题标题】:EventSource permanent auto reconnectionEventSource 永久自动重新连接
【发布时间】:2014-03-16 20:59:18
【问题描述】:

我在我的项目前端使用 JavaScript EventSource。

有时,浏览器与服务器之间的连接失败或服务器崩溃。在这些情况下,EventSource 会在 3 秒后尝试重新连接,如文档中所述。

但它只尝试一次。如果仍然没有连接,EventSource 将停止尝试重新连接,用户必须刷新浏览器窗口才能再次连接。

如何防止这种行为?我需要 EventSource 来尝试永远重新连接,而不仅仅是一次。

浏览器是火狐。

【问题讨论】:

    标签: javascript server-sent-events server-push


    【解决方案1】:

    我通过实施保活系统来解决这个问题;如果浏览器重新连接对我来说一切都很好,但我认为有时它不会工作,而且不同的浏览器可能会有不同的行为。

    我在本书的第五章中花了相当多的篇幅(Blatant plug,在 O'Reilly 上找到它:Data Push Applications Using HTML5 SSE),但是如果您想要一个不需要任何后端的非常简单的解决方案更改,设置一个全局计时器,该计时器将在 30 秒后触发。如果它触发,那么它将杀死 EventSource 对象并创建另一个。谜题的最后一部分在于您的事件侦听器:每次从后端获取数据时,终止计时器并重新创建它。 IE。只要您至少每 30 秒获取一次新数据,计时器就永远不会触发。

    这里有一些最小的代码来显示这一点:

    var keepAliveTimer = null;
    
    function gotActivity(){
      if(keepaliveTimer != null)clearTimeout(keepaliveTimer);
      keepaliveTimer = setTimeout(connect, 30 * 1000);
    }
    
    function connect(){
      gotActivity();
      var es = new EventSource("/somewhere/");
      es.addEventListener('message', function(e){
        gotActivity();
        },false);
    }
    ...
    connect();
    

    另外请注意,我在连接之前调用了 gotActivity()。否则,失败的连接或在它有机会提供任何数据之前就中断的连接将被忽视。

    顺便说一句,如果您也可以更改后端,那么在 25-30 秒的安静后发送一条空白消息(“心跳”)是值得的。否则前端将不得不假设后端已经死亡。当然,如果您的服务器发送间隔不超过 25-30 秒的常规消息,则无需执行任何操作。

    如果您的应用程序依赖于 Event-Last-Id 标头,请意识到您的保活系统必须对此进行模拟;这涉及到更多。

    【讨论】:

    • 好吧,gotActivity 调用应该在“new EventSource”之前,但在一些小修复之后一切正常。
    • @johnfound 在new EventSource() 调用之前需要gotActivity() 的问题案例是什么?
    • 我的JS知识很少。但是在我的测试中,如果新的事件源无法连接,那么 gotActivity 根本没有执行,所以计时器没有重新启动以进行另一次尝试....
    • @johnfound 谢谢。我明白你的意思,我已经调整了我的答案。 (我有它,所以如果客户端不支持 EventSource,或者我添加的任何后备,它不会浪费精力每 30 秒再次尝试一次;但这是一个更小的问题。)
    • 不错的设计。但是,如果服务器需要使用凭据登录,并且服务器重新启动(使任何会话身份验证 cookie 无效),事情就会变得更加复杂。 :-)
    【解决方案2】:

    根据我的经验,如果出现网络级错误,浏览器通常会重新连接,但如果服务器响应 HTTP 错误(例如状态 500)则不会。

    我们的团队制作了一个简单的包装库,可以在所有情况下重新连接:reconnecting-eventsource。也许有用。

    【讨论】:

    • 非常有帮助,Chrome 88 不需要这个,因为断开连接时 readystate 为 0。 FF 85 确实如此,因为 readystate 似乎在出错时立即变为 2(关闭)。此包装器正确尝试在 readystate=2(FF) 上重新打开,因此可以在两个浏览器上安全使用。
    【解决方案3】:

    下面,我演示了一种以合理的速率重新连接的方法,永远。

    此代码使用去抖动功能以及重新连接间隔加倍。在我的测试中,它运行良好。它以 1 秒、4、8、16 秒...最多 64 秒连接,在此期间它会以相同的速率继续重试。

    function isFunction(functionToCheck) {
      return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
    }
    
    function debounce(func, wait) {
        var timeout;
        var waitFunc;
    
        return function() {
            if (isFunction(wait)) {
                waitFunc = wait;
            }
            else {
                waitFunc = function() { return wait };
            }
    
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                func.apply(context, args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, waitFunc());
        };
    }
    
    // reconnectFrequencySeconds doubles every retry
    var reconnectFrequencySeconds = 1;
    var evtSource;
    
    var reconnectFunc = debounce(function() {
        setupEventSource();
        // Double every attempt to avoid overwhelming server
        reconnectFrequencySeconds *= 2;
        // Max out at ~1 minute as a compromise between user experience and server load
        if (reconnectFrequencySeconds >= 64) {
            reconnectFrequencySeconds = 64;
        }
    }, function() { return reconnectFrequencySeconds * 1000 });
    
    function setupEventSource() {
        evtSource = new EventSource(/* URL here */); 
        evtSource.onmessage = function(e) {
          // Handle even here
        };
        evtSource.onopen = function(e) {
          // Reset reconnect frequency upon successful connection
          reconnectFrequencySeconds = 1;
        };
        evtSource.onerror = function(e) {
          evtSource.close();
          reconnectFunc();
        };
    }
    setupEventSource();
    

    【讨论】:

      猜你喜欢
      • 2012-10-28
      • 2016-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-18
      • 2015-05-01
      • 2017-03-11
      • 2017-04-19
      相关资源
      最近更新 更多