习惯于
NodeJS,Socket.木卫一
问题
假设有两个用户U1&;U2,通过插座连接到应用程序.伊奥.算法如下所示:
- U1完全失go 互联网连接(例如关闭互联网)
- U2向U1发送消息.
- U1尚未收到该消息,因为互联网已关闭
- Server通过心跳超时检测U1断开
- U1重新连接到插座.木卫一
- U1永远不会收到U2的信息——我想它在第4步中丢失了.
可能的解释
我想我明白为什么会这样:
- 在步骤4,Server也将套接字实例和消息队列杀死到U1
- 此外,在步骤5、U1和Server中,创建新连接(它不会被重用),所以即使消息仍在排队,之前的连接也会丢失.
需要帮助吗
如何防止这种数据丢失?我必须使用hearbeats,因为我不会让人们永远挂在应用程序中.此外,我还必须提供重新连接的可能性,因为当我部署新版本的应用程序时,我希望零停机时间.
另外,我称之为"消息"的东西不仅仅是我可以存储在数据库中的文本消息,而是有价值的系统消息,必须保证传递,否则UI会出错.
谢谢
增补1
我已经有了一个用户帐户系统.此外,我的应用程序已经很复杂了.添加离线/在线状态不会有帮助,因为我已经有了这种东西.问题不一样.
看看第二步.在这一步中,我们技术上是cannot say if U1 goes offline,他只是失go 了连接,比如说2秒钟,可能是因为糟糕的互联网.所以U2向他发送了一条消息,但U1没有收到,因为他仍然无法上网(第3步).第4步需要检测离线用户,比如说,超时时间为60秒.最后,再过10秒钟,U1的互联网连接就接通了,他重新连接到了插座上.伊奥.但来自U2的消息在空间中丢失,因为服务器U1上的连接已被超时断开.
这就是问题所在,我不想100%交货.
解决方案
- 在{}用户中收集一个emit(emit name and data),由随机emitID标识.emits
- 确认客户端的emit(使用emitID将emit发送回服务器)
- 如果确认-从emitID标识的{}中删除对象
- 如果用户重新连接-判断该用户的{},并对{}中的每个对象执行步骤1
- 断开或/和连接时,如有必要,为用户刷新{}
// Server
const pendingEmits = {};
socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });
// Client
socket.on('something', () => {
socket.emit('confirm', emitID);
});
解决方案 2 (kinda)
Added 1 Feb 2020.
虽然这并不是WebSocket的真正解决方案,但有些人可能仍然觉得它很方便.我们从WebSocket迁移到SSE+Ajax.SSE允许您从客户端连接,以保持持久的TCP连接,并实时接收来自服务器的消息.要将消息从客户端发送到服务器,只需使用Ajax.虽然存在延迟和开销等缺点,但SSE保证了可靠性,因为它是TCP连接.
由于我们使用Express,我们使用SSE https://github.com/dpskvn/express-sse的这个库,但您可以 Select 适合您的库.
IE和大多数Edge版本都不支持SSE,因此需要polyfill:https://github.com/Yaffle/EventSource.