node.js/socket.io:重新连接时刷新 table?
node.js/socket.io: Refresh table on reconnect?
我有 node.js 和 socket.io 对数据表 table 执行实时更新。编辑一行时,其他客户端无需刷新即可看到该行的更新。
一切正常,但我不知道如何在断开连接后 node/socket 重新连接到服务器时 自动刷新 table。
目前,情况是这样的:
- 转到包含 table 的页面
- 让设备 sleep/disconnect 来自
服务器
- 打开设备并查看包含 table
的页面
- socket.io 重新连接到服务器,但 table 不会刷新以获取最新更改。
如何让 socket.io 在重新连接到服务器时刷新 table? 理想情况下,过程如下所示:
- 打开设备
- socket.io 重新连接到服务器,触发 CSS "Loading..." 覆盖以防止用户对 table
进行更改
- socket.io刷新table显示最新内容
- CSS "Loading..." 覆盖关闭
服务器端脚本:
console.log('Starting the server');
var app = require('express')();
var express = require('express');
var fs = require("fs");
var server = require('https').createServer(SSL, app);
var io = require('socket.io')(server);
server.listen(100);
io.on('connection', function (socket) {
/*socket.emit('news', { hello: 'world' });
/socket.on('my other event', function (data) {
console.log(data);
});*/
});
io.set('authorization', function(handshakeData, accept) {
console.log('io.authorization called');
accept(null, true);
} );
var lockedRowIDs = [];
io.sockets.on('connection', function(socket) {
console.log('io.connection called');
socket.on('lock', function(rowID) {
console.log('Lock event for rowID: '+rowID);
lock(socket, rowID);
lockedRowIDs.push(rowID);
} );
socket.on('clientJoin', function(username) {
console.log('clientJoin event received');
socket.join('clients');
if (typeof lockedRowIDs !== 'undefined' && lockedRowIDs.length > 0) {
socket.emit('lockedRows', lockedRowIDs);
}
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event for rowID: '+rowID);
unlock(socket, rowID);
removeItemFromArray(lockedRowIDs, rowID);
} );
socket.on('updateData', function(json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData event for rowID: '+json.row['DT_RowId']);
}
updateData(socket, json, action, id);
} );
} );
function lock(socket, rowID) {
socket.broadcast.to('clients').emit('lock', rowID);
setTimeout(function() {
io.sockets.in('clients').emit('timeout', rowID);
removeItemFromArray(lockedRowIDs, rowID);
},
180000);
}
function unlock(socket, rowID) {
socket.broadcast.to('clients').emit('unlock', rowID);
}
function removeItemFromArray(array, item) {
console.log('removeItemFromArray called with item: '+item);
for(var i = array.length - 1; i >= 0; i--) {
if(array[i] === item) {
array.splice(i, 1);
}
}
}
function updateData(socket, json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData called with rowID:'+json.row['DT_RowId']);
}
socket.broadcast.to('clients').emit('updateData', json, action, id);
}
客户端脚本:
var socket = io('https://www.***.com:100');
socket.on('connect', function() {
console.log('Connected');
socket.emit('clientJoin');
} );
socket.on('lock', function(rowID) {
console.log('Lock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.addClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().addClass('locked');
/* Pagenation fix End */
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
} );
socket.on('timeout', function(rowID) {
console.log('Time out event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
/* Check if the editor corresponds to the timed out rowID - start */
var modifier = editor.modifier();
if (modifier) {
var data = table.row(modifier).data();
console.log('rowID is '+data.DT_RowId);
if (data.DT_RowId == rowID) {
console.log('Timed out rowID: '+rowID+' matches Editor rowID: '+data.DT_RowId+'. Closing Editor now.');
editor.close();
}
else {
console.log('Timed out rowID: '+rowID+' does not match Editor rowID: '+data.DT_RowId+'. Keeping Editor open.');
}
}
/* Check if the editor corresponds to the timed out rowID - end */
} );
socket.on('lockedRows', function (rowIDs) {
console.log('Iterate through the list of rows and mark it as locked');
table = $('#example').DataTable();
rowCount = rowIDs.length;
console.log('Row count: '+rowCount);
for (var i=0; i<rowCount; i++) {
console.log(rowIDs[i]);
row = $("tr[id='"+rowIDs[i]+"']");
row.addClass('locked');
table.row('#'+rowIDs[i]).nodes().to$().addClass('locked');
}
} );
socket.on('updateData', function(json, action, id) {
if (action == "create" || action == "edit") {
var DT_RowId = json.row['DT_RowId'];
console.log('updateData socket event for rowID: '+DT_RowId+' and action: '+action);
}
var table = $('table#example').DataTable();
if (action == "edit") {
var editedRow = table.row('#'+DT_RowId).nodes().to$();
table.row(editedRow).data(json.row).draw();
console.log('Row updated');
}
if (action == "create") {
console.log('Row created');
table.row.add(json.row).draw();
}
if (action == "remove") {
var removedRow = table.row('#'+id).nodes().to$();
table.row(removedRow).remove().draw();
console.log('Row removed with id '+id);
}
} );
/* Ajax request has been completed, data retrieved from the server */
editor.on('postSubmit', function(e,json, data, action) {
console.log('Post submit');
console.log(data);
console.log('With JSON:');
console.log(json);
console.log('With action:');
console.log(action);
if (action == "create" || action== "edit") {
if (json.row){
console.log('rowID from JSON: '+json.row['DT_RowId']);
socket.emit('updateData', json, action);
}
}
if (action == "remove") {
console.log('rowID from JSON: '+data.id[0]);
socket.emit('updateData', null, action, data.id[0]);
}
} );
editor.on('close', function(e) {
console.log('Close event');
console.log(e);
var modifier = editor.modifier();
console.log(modifier)
if (modifier !== null) {
console.log('Inside modifier')
table = $('#example').DataTable();
if (table.row(modifier).node()) {
rowID = table.row(modifier).node().id;
console.log('rowID='+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
table.row('#'+rowID).nodes().to$().removeClass('locked');
socket.emit('unlock', rowID);
}
}
} );
我想你知道,当你连接时,你会保持数据是最新的,但如果你暂时断开连接然后重新连接,你可能会错过一些数据更新。
有很多可能的策略来处理这个问题。
暴力破解。 重新连接后,获取所有数据的新副本,就好像设备刚刚打开一样。效率较低,但易于实施。
事务 ID 或事务时间。 每次服务器发送更新时,它都会随该更新一起发送事务 ID 或事务服务器时间。然后客户端跟踪它收到的最后一个交易 ID 或交易时间。当它重新连接时,它会发送一条初始消息,其中包含最后一个事务 ID 或事务时间,询问自上次事务以来发生的任何更新。服务器然后可以查看其数据库以查看是否有任何更新的事务,如果有,则将它们发送给该客户端。这需要跟踪数据库中的事务。服务器可能 return "transaction id not supported" 类型的响应是很常见的,然后强制客户端从头开始进行暴力更新。这是一个后备站,以防数据库重建或服务器崩溃导致旧事务值丢失。
真正的同步。客户端重新连接,然后与服务器进行真正的同步,客户端基本上说 "this is what I have, do you have anything newer"。为了提高效率,许多同步系统通过实现选项 2 中描述的事务 id 来解决这个问题,但当然还有许多其他方法可以进行同步。如果客户端在断开连接时也可能更改数据,则真正的同步更有用。
作为实施选项 2 的一种简化方式,如果您的数据库尚不支持日志事务的概念,那么某些服务器将在内存中实施事务日志,用于跟踪最后 N 小时的事务。只要服务器保持运行并且断开连接的持续时间不超过 N 小时,就可以从内存事务日志中满足对新数据的请求(这是高效的)。如果服务器重新启动或客户端离开时间超过 N 小时,则客户端将被迫进行暴力更新,这与客户端关闭电源然后重新打开电源时所做的相同(丢失所有数据的先验知识)。
当然,如果数据库有多个用户,那么事务日志就得由数据库自己实现,以确保它包含所有可能的事务。
我有 node.js 和 socket.io 对数据表 table 执行实时更新。编辑一行时,其他客户端无需刷新即可看到该行的更新。
一切正常,但我不知道如何在断开连接后 node/socket 重新连接到服务器时 自动刷新 table。
目前,情况是这样的:
- 转到包含 table 的页面
- 让设备 sleep/disconnect 来自 服务器
- 打开设备并查看包含 table 的页面
- socket.io 重新连接到服务器,但 table 不会刷新以获取最新更改。
如何让 socket.io 在重新连接到服务器时刷新 table? 理想情况下,过程如下所示:
- 打开设备
- socket.io 重新连接到服务器,触发 CSS "Loading..." 覆盖以防止用户对 table 进行更改
- socket.io刷新table显示最新内容
- CSS "Loading..." 覆盖关闭
服务器端脚本:
console.log('Starting the server');
var app = require('express')();
var express = require('express');
var fs = require("fs");
var server = require('https').createServer(SSL, app);
var io = require('socket.io')(server);
server.listen(100);
io.on('connection', function (socket) {
/*socket.emit('news', { hello: 'world' });
/socket.on('my other event', function (data) {
console.log(data);
});*/
});
io.set('authorization', function(handshakeData, accept) {
console.log('io.authorization called');
accept(null, true);
} );
var lockedRowIDs = [];
io.sockets.on('connection', function(socket) {
console.log('io.connection called');
socket.on('lock', function(rowID) {
console.log('Lock event for rowID: '+rowID);
lock(socket, rowID);
lockedRowIDs.push(rowID);
} );
socket.on('clientJoin', function(username) {
console.log('clientJoin event received');
socket.join('clients');
if (typeof lockedRowIDs !== 'undefined' && lockedRowIDs.length > 0) {
socket.emit('lockedRows', lockedRowIDs);
}
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event for rowID: '+rowID);
unlock(socket, rowID);
removeItemFromArray(lockedRowIDs, rowID);
} );
socket.on('updateData', function(json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData event for rowID: '+json.row['DT_RowId']);
}
updateData(socket, json, action, id);
} );
} );
function lock(socket, rowID) {
socket.broadcast.to('clients').emit('lock', rowID);
setTimeout(function() {
io.sockets.in('clients').emit('timeout', rowID);
removeItemFromArray(lockedRowIDs, rowID);
},
180000);
}
function unlock(socket, rowID) {
socket.broadcast.to('clients').emit('unlock', rowID);
}
function removeItemFromArray(array, item) {
console.log('removeItemFromArray called with item: '+item);
for(var i = array.length - 1; i >= 0; i--) {
if(array[i] === item) {
array.splice(i, 1);
}
}
}
function updateData(socket, json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData called with rowID:'+json.row['DT_RowId']);
}
socket.broadcast.to('clients').emit('updateData', json, action, id);
}
客户端脚本:
var socket = io('https://www.***.com:100');
socket.on('connect', function() {
console.log('Connected');
socket.emit('clientJoin');
} );
socket.on('lock', function(rowID) {
console.log('Lock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.addClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().addClass('locked');
/* Pagenation fix End */
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
} );
socket.on('timeout', function(rowID) {
console.log('Time out event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
/* Check if the editor corresponds to the timed out rowID - start */
var modifier = editor.modifier();
if (modifier) {
var data = table.row(modifier).data();
console.log('rowID is '+data.DT_RowId);
if (data.DT_RowId == rowID) {
console.log('Timed out rowID: '+rowID+' matches Editor rowID: '+data.DT_RowId+'. Closing Editor now.');
editor.close();
}
else {
console.log('Timed out rowID: '+rowID+' does not match Editor rowID: '+data.DT_RowId+'. Keeping Editor open.');
}
}
/* Check if the editor corresponds to the timed out rowID - end */
} );
socket.on('lockedRows', function (rowIDs) {
console.log('Iterate through the list of rows and mark it as locked');
table = $('#example').DataTable();
rowCount = rowIDs.length;
console.log('Row count: '+rowCount);
for (var i=0; i<rowCount; i++) {
console.log(rowIDs[i]);
row = $("tr[id='"+rowIDs[i]+"']");
row.addClass('locked');
table.row('#'+rowIDs[i]).nodes().to$().addClass('locked');
}
} );
socket.on('updateData', function(json, action, id) {
if (action == "create" || action == "edit") {
var DT_RowId = json.row['DT_RowId'];
console.log('updateData socket event for rowID: '+DT_RowId+' and action: '+action);
}
var table = $('table#example').DataTable();
if (action == "edit") {
var editedRow = table.row('#'+DT_RowId).nodes().to$();
table.row(editedRow).data(json.row).draw();
console.log('Row updated');
}
if (action == "create") {
console.log('Row created');
table.row.add(json.row).draw();
}
if (action == "remove") {
var removedRow = table.row('#'+id).nodes().to$();
table.row(removedRow).remove().draw();
console.log('Row removed with id '+id);
}
} );
/* Ajax request has been completed, data retrieved from the server */
editor.on('postSubmit', function(e,json, data, action) {
console.log('Post submit');
console.log(data);
console.log('With JSON:');
console.log(json);
console.log('With action:');
console.log(action);
if (action == "create" || action== "edit") {
if (json.row){
console.log('rowID from JSON: '+json.row['DT_RowId']);
socket.emit('updateData', json, action);
}
}
if (action == "remove") {
console.log('rowID from JSON: '+data.id[0]);
socket.emit('updateData', null, action, data.id[0]);
}
} );
editor.on('close', function(e) {
console.log('Close event');
console.log(e);
var modifier = editor.modifier();
console.log(modifier)
if (modifier !== null) {
console.log('Inside modifier')
table = $('#example').DataTable();
if (table.row(modifier).node()) {
rowID = table.row(modifier).node().id;
console.log('rowID='+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
table.row('#'+rowID).nodes().to$().removeClass('locked');
socket.emit('unlock', rowID);
}
}
} );
我想你知道,当你连接时,你会保持数据是最新的,但如果你暂时断开连接然后重新连接,你可能会错过一些数据更新。
有很多可能的策略来处理这个问题。
暴力破解。 重新连接后,获取所有数据的新副本,就好像设备刚刚打开一样。效率较低,但易于实施。
事务 ID 或事务时间。 每次服务器发送更新时,它都会随该更新一起发送事务 ID 或事务服务器时间。然后客户端跟踪它收到的最后一个交易 ID 或交易时间。当它重新连接时,它会发送一条初始消息,其中包含最后一个事务 ID 或事务时间,询问自上次事务以来发生的任何更新。服务器然后可以查看其数据库以查看是否有任何更新的事务,如果有,则将它们发送给该客户端。这需要跟踪数据库中的事务。服务器可能 return "transaction id not supported" 类型的响应是很常见的,然后强制客户端从头开始进行暴力更新。这是一个后备站,以防数据库重建或服务器崩溃导致旧事务值丢失。
真正的同步。客户端重新连接,然后与服务器进行真正的同步,客户端基本上说 "this is what I have, do you have anything newer"。为了提高效率,许多同步系统通过实现选项 2 中描述的事务 id 来解决这个问题,但当然还有许多其他方法可以进行同步。如果客户端在断开连接时也可能更改数据,则真正的同步更有用。
作为实施选项 2 的一种简化方式,如果您的数据库尚不支持日志事务的概念,那么某些服务器将在内存中实施事务日志,用于跟踪最后 N 小时的事务。只要服务器保持运行并且断开连接的持续时间不超过 N 小时,就可以从内存事务日志中满足对新数据的请求(这是高效的)。如果服务器重新启动或客户端离开时间超过 N 小时,则客户端将被迫进行暴力更新,这与客户端关闭电源然后重新打开电源时所做的相同(丢失所有数据的先验知识)。
当然,如果数据库有多个用户,那么事务日志就得由数据库自己实现,以确保它包含所有可能的事务。