用过的
NodeJS,Socket.io
问题
想象有 2 个用户U1和U2,通过 Socket.io 连接到一个应用程序。算法如下:
- U1完全失去互联网连接(例如关闭互联网)
- U2向U1发送消息。
- U1尚未收到消息,因为 Internet 已关闭
- 服务器通过心跳超时检测U1断线
- U1重新连接到 socket.io
- U1永远不会收到来自U2的消息- 我猜它在第 4 步丢失了。
可能的解释
我想我明白为什么会这样:
- 在第 4 步上,服务器也终止了套接字实例和发送到U1的消息队列
- 此外,在第 5 步U1和服务器创建新连接(它不被重用),所以即使消息仍在排队,之前的连接无论如何都会丢失。
需要帮忙
如何防止这种数据丢失?我必须使用心跳,因为我不会让人们永远挂在应用程序中。此外,我仍然必须提供重新连接的可能性,因为当我部署新版本的应用程序时,我希望零停机时间。
PS 我称之为“消息”的东西不仅仅是我可以存储在数据库中的文本消息,而是有价值的系统消息,必须保证交付,否则 UI 会搞砸。
谢谢!
加法1
我已经有一个用户帐户系统。而且,我的应用程序已经很复杂了。添加离线/在线状态无济于事,因为我已经有了这种东西。问题是不同的。
查看第 2 步。在这一步中,我们在技术上不能说 U1 是否离线,他只是失去连接让我们说 2 秒钟,可能是因为网络不好。所以 U2 向他发送了一条消息,但 U1 没有收到它,因为他的互联网仍然关闭(第 3 步)。需要第 4 步来检测离线用户,假设超时为 60 秒。最终,再过 10 秒,U1 的互联网连接就建立起来了,他重新连接到了 socket.io。但是来自 U2 的消息在空间中丢失了,因为服务器 U1 因超时而断开连接。
这就是问题所在,我不想 100% 交货。
解决方案
- 在 {} 用户中收集发射(发射名称和数据),由随机发射 ID 标识。发送发射
- 在客户端确认发射(使用 emitID 将发射发送回服务器)
- 如果确认 - 从由 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(有点)
2020 年 2 月 1 日添加。
虽然这并不是 Websockets 的真正解决方案,但有人可能仍然觉得它很方便。我们从 Websockets 迁移到 SSE + Ajax。SSE 允许您从客户端连接以保持持久的 TCP 连接并实时接收来自服务器的消息。要将消息从客户端发送到服务器 - 只需使用 Ajax。有延迟和开销等缺点,但 SSE 保证可靠性,因为它是 TCP 连接。
由于我们使用 Express,因此我们将此库用于 SSE https://github.com/dpskvn/express-sse,但您可以选择适合您的库。
IE 和大多数 Edge 版本不支持 SSE,因此您需要一个 polyfill:https ://github.com/Yaffle/EventSource 。