我在项目前端使用JavaScript EventSource。
有时,浏览器与服务器之间的连接失败或服务器崩溃。在这些情况下,EventSource会在3秒后尝试重新连接,如文档中所述。
但它只尝试一次。如果仍然没有连接,则EventSource将停止尝试重新连接,并且用户必须刷新浏览器窗口才能再次连接。
如何防止这种行为?我需要EventSource来尝试永远重新连接,而不仅仅是一次。
浏览器是Firefox。
答案 0 :(得分:5)
我通过实现保持活动系统来处理这个问题;如果浏览器重新连接我,这一切都很好,但我认为有时它不起作用,而且不同的浏览器可能表现不同。
我在本书的第五章(Blatant plug,在O'Reilly这里找到它:Data Push Applications Using HTML5 SSE)上花了几页,但如果你想要一个不需要任何回复的非常简单的解决方案 - 结束更改,设置一个全局计时器,在30秒之后触发。如果它触发,那么它将终止EventSource对象并创建另一个。最后一个难题是在你的事件监听器中:每次从后端获取数据时,杀死计时器并重新创建它。即只要您至少每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标头,请认识到您的保持活动系统必须模拟这个;得到更多参与。
答案 1 :(得分:1)
以下,我演示了一种以合理的速率永久地重新连接的方法。
此代码使用防抖功能以及重新连接间隔加倍。在我的测试过程中,它运作良好。它以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();
答案 2 :(得分:0)
根据我的经验,浏览器通常会在出现网络级错误时重新连接,但如果服务器响应HTTP错误(例如状态500)则不会重新连接。
我们的团队制作了一个简单的包装器库,可以在所有情况下重新连接:reconnecting-eventsource。也许有帮助。