用过的
的NodeJS,Socket.io
问题
试想一下,有2个用户U1和U2,连接到通过Socket.io的应用程序。 该算法如下:
- U1完全失去互联网连接(例如,网络交换机关闭)
- U2将消息发送到U1。
- U1没有收到消息,但因为互联网是下降
- 服务器通过心跳超时检测U1断开
- U1重新连接到socket.io
- U1从来没有从U2收到消息-这是失去了对第4步,我猜。
可能的解释
我想我明白为什么会发生:
- 第4步服务器杀死Socket实例和消息U1的队列以及
- 而且在步骤5 U1和服务器创建新的连接(这是不重复使用),所以即使邮件仍然是排队,前面的连接反正丢失。
需要帮忙
我怎样才能防止这种数据丢失的? 我必须使用hearbeats,因为我没有人应用挂起,直到永远。 此外,我还必须给出一个可能性重新连接,因为当我部署应用程序的新版本,我想零停机时间。
PS我所说的“消息”的事情不只是一条短信,我可以在数据库中存储,而是宝贵的系统消息,该消息传递必须保证,或最多UI螺丝。
谢谢!
除了1
我已经有一个用户帐户系统。 此外,我的应用程序已经是复杂的。 添加离线/在线状态不会帮助,因为我已经有这种东西。 问题是不同的。
看看第2步在这一步,我们在技术上不能说如果U1下线时 ,他只是失去连接可以说,可能是因为不良互联网的2秒。 所以U2送他的消息,但由于互联网仍然下跌对他(步骤3)U1不会收到它。 需要步骤4,检测脱机用户,可以说,超时时间为60秒。 最终,在U1 10秒钟的互联网连接起来,他重新连接到socket.io。 但是,从U2的消息丢失在太空因为服务器U1被超时断开。
这就是问题所在,我wan't 100%交付。
解
- 收集在{}用户的EMIT(发射名称和数据),通过随机emitID识别。 发送EMIT
- 确认客户端上的EMIT(发送发射回服务器emitID)
- 如果确认 - 删除{}对象通过emitID鉴定
- 如果用户重新连接 - 校验{}为该用户和循环通过它在执行步骤1为每个对象{}
当断开连接和/或连接的冲洗{}为用户如果有必要
//服务器常量pendingEmits = {};
socket.on( '重新连接',()=> resendAllPendingLimits); socket.on( '确认',(emitID)=> {删除(pendingEmits [emitID]);});
//客户socket.on( '某物',()=> {socket.emit( '确认',emitID);});
其他人在这个在其他的答案和评论暗示,但根本的问题是,Socket.IO只是一个传递机制,你不能可靠传送仅仅依赖于它。 谁肯定知道消息已被成功地传递到客户端的唯一的人是客户本身 。 对于这种系统,我会建议做如下声明:
- 消息不是直接发送到客户端; 相反,他们会发送到服务器和存储在某些类型的数据存储。
- 客户有责任问:“我错过了什么”,当他们重新连接,并将查询在数据存储中存储的信息更新他们的状态。
- 如果在接收方客户端连接到的信息发送到服务器,该消息将被实时发送到客户端。
当然,这取决于应用程序的需求,你可以在这首曲子的片段 - 例如,你可以使用,比如说,一个Redis的列表或分类设置的信息,并清除出来,如果你知道一个事实,一个客户端了至今。
这里有几个例子:
快乐路径 :
- U1和U2都连接到该系统。
- U2将消息发送到U1应该接收服务器。
- 服务器在某种持久性存储的存储信息,将其标记为与某种时间戳或顺序编号的U1。
- 服务器经由Socket.IO将消息发送到U1。
- U1的客户端确认(或许通过Socket.IO回调),它接收到的消息。
- 服务器删除从数据存储中的持久消息。
离线路径 :
- U1失去互联网连接。
- U2将消息发送到U1应该接收服务器。
- 服务器在某种持久性存储的存储信息,将其标记为与某种时间戳或顺序编号的U1。
- 服务器经由Socket.IO将消息发送到U1。
- U1的客户端不确认收货,因为它们是离线。
- 也许U2 U1发送几个消息; 他们都得到存储在以同样的方式将数据存储。
- 当U1重新连接,它要求服务器“的最后一条消息我看到的是X /我有状态X,我错过了什么。”
- 服务器发送U1的所有它的数据存储错过基于U1的请求消息
- U1的客户端确认收到和服务器中删除从数据存储中的消息。
如果你绝对要保证交付,然后它来设计你的系统,这样的方式连接,实际上并不事情是很重要的,而且实时传送是一个简单的奖金 ; 这几乎总是涉及某种形式的数据存储。 作为user568109在注释提到的,有邮件系统抽象远的存储和递送所述消息,并且它可以是值得探讨这样的预建溶液。 (你可能还是要自己编写Socket.IO整合。)
如果你不感兴趣,在数据库中存储的信息,您可以逃脱它们存储在本地阵列; 服务器尝试发送U1的消息,并将其存储在“等待消息”的列表,直到U1的客户确认其收到。 如果客户端处于脱机状态,那么当它回来了它可以告诉服务器“嘿,我被中断,请给我什么,我错过了”,而服务器可以通过这些信息进行迭代。
幸运的是,Socket.IO提供了一种机制,允许客户端为“响应”,看起来像本地JS回调的消息。 下面是一些伪代码:
// server
pendingMessagesForSocket = [];
function sendMessage(message) {
pendingMessagesForSocket.push(message);
socket.emit('message', message, function() {
pendingMessagesForSocket.remove(message);
}
};
socket.on('reconnection', function(lastKnownMessage) {
// you may want to make sure you resend them in order, or one at a time, etc.
for (message in pendingMessagesForSocket since lastKnownMessage) {
socket.emit('message', message, function() {
pendingMessagesForSocket.remove(message);
}
}
});
// client
socket.on('connection', function() {
if (previouslyConnected) {
socket.emit('reconnection', lastKnownMessage);
} else {
// first connection; any further connections means we disconnected
previouslyConnected = true;
}
});
socket.on('message', function(data, callback) {
// Do something with `data`
lastKnownMessage = data;
callback(); // confirm we received the message
});
这是颇为相似,最后建议,根本没有持久性数据存储。
您还可能有兴趣在概念事件采购 。
它似乎是你已经拥有的用户帐户系统。 你知道哪一个账户在线/离线,您可以处理连接/断开事件:
因此,解决办法是,增加在线/离线和数据库中的每个用户离线消息:
chatApp.onLogin(function (user) {
user.readOfflineMessage(function (msgs) {
user.sendOfflineMessage(msgs, function (err) {
if (!err) user.clearOfflineMessage();
});
})
});
chatApp.onMessage(function (fromUser, toUser, msg) {
if (user.isOnline()) {
toUser.sendMessage(msg, function (err) {
// alert CAN NOT SEND, RETRY?
});
} else {
toUser.addToOfflineQueue(msg);
}
})
看看这里: 处理浏览器重装socket.io 。
我想你可以使用我想出了解决方案。 如果你正确地修改它,它应该工作,只要你想。
我想你想要的是为每个用户可重复使用的插座,这样的:
客户:
socket.on("msg", function(){
socket.send("msg-conf");
});
服务器:
// Add this socket property to all users, with your existing user system
user.socket = {
messages:[],
io:null
}
user.send = function(msg){ // Call this method to send a message
if(this.socket.io){ // this.io will be set to null when dissconnected
// Wait For Confirmation that message was sent.
var hasconf = false;
this.socket.io.on("msg-conf", function(data){
// Expect the client to emit "msg-conf"
hasconf = true;
});
// send the message
this.socket.io.send("msg", msg); // if connected, call socket.io's send method
setTimeout(function(){
if(!hasconf){
this.socket = null; // If the client did not respond, mark them as offline.
this.socket.messages.push(msg); // Add it to the queue
}
}, 60 * 1000); // Make sure this is the same as your timeout.
} else {
this.socket.messages.push(msg); // Otherwise, it's offline. Add it to the message queue
}
}
user.flush = function(){ // Call this when user comes back online
for(var msg in this.socket.messages){ // For every message in the queue, send it.
this.send(msg);
}
}
// Make Sure this runs whenever the user gets logged in/comes online
user.onconnect = function(socket){
this.socket.io = socket; // Set the socket.io socket
this.flush(); // Send all messages that are waiting
}
// Make sure this is called when the user disconnects/logs out
user.disconnect = function(){
self.socket.io = null; // Set the socket to null, so any messages are queued not send.
}
然后套接字队列断开之间保留。
请确保它的每个用户节省socket
属性数据库,并让你的用户原型的方法的一部分。 该数据库没有关系,只是保存它,但是你已经保存的用户。
这将通过作为发送标记消息之前需要来自客户端的确认避免在additon 1中提到的问题。 如果你真的想,你可以给每个消息的ID,并在客户端发送消息ID msg-conf
,然后检查。
在这个例子中, user
是模板的用户,所有用户都从,或喜欢的用户原型复制。
注意:这并没有经过测试。
一直在研究这个东西近来,认为不同的路径可能会更好。
试着看一下Azure的服务总线,疑问句和话题照顾离线状态。 消息等待用户回来,然后他们得到的消息。
是运行队列中,但每百万次操作的一个基本的队列它像$ 0.05成本,使开发成本将是从几个小时的工作更需要写一个排队系统。 https://azure.microsoft.com/en-us/pricing/details/service-bus/
和天蓝色巴士有PHP,C#,Xarmin,Anjular,Java脚本等库和示例
因此,服务器发送消息,并不需要担心他们的跟踪。 客户端可以使用消息发回也仿佛必要手段可以处理负载平衡。