您需要小心客戶端啓動新請求時發生的情況,因爲文件在此時間段內可能會更改。爲了照顧這個
一種方式是客戶端首先查詢當前文件狀態:
GET /file/timestamp
-> Server returns the timestamp
GET /file/update?after=[timestamp]
-> Server checks whether the file has changed after the timestamp.
If it has, the server sends the response immediately.
Otherwise insert the client into a queue.
You don't need to save the timestamp in the queue.
If the server notices a change it notifies the clients.
現在,因爲多個客戶端的服務器不應該做在客戶端請求處理的輪詢。而是有一個單獨的對象來處理輪詢。
根據您是否有一個或多個需要監視的文件,您最終可能會得到一個簡單或複雜的實現。總之,儘管您可能想將fs.watchFile換成EventEmitter,以便對文件所做的更改將發出change
事件。
一個天真的實現是:
var watcher = new EventEmitter();
// Get the initial status
fs.lstat('test.file', function(err, stat) {
if(err) return console.error(err);
watcher.stat = stat;
});
// Start watching the file and emit an event whenever the file changes.
fs.watchFile('test.file', function(curr, prev) {
console.log('File changed');
watcher.stat = curr;
watcher.emit('change', curr);
});
有了這些到位您的請求處理程序看起來像沿着線的東西:
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });
var timeout;
var onChange = function(stat) {
// Prevent timeout from triggering
clearTimeout(timeout);
console.log('File changed at ' + stat.mtime);
res.end(
'Changed at ' + stat.mtime + '. ' +
'<a href="?after=' + stat.mtime.getTime() + '">Poll again</a>');
};
var after = url.parse(req.url).query || '';
after = after.split('=');
console.dir(after);
if(after.length < 2 || after[1] < watcher.stat.mtime.getTime()) {
console.log('Initial request or file changed before request');
return onChange(watcher.stat);
}
console.log('Polling request.');
watcher.once('change', onChange);
timeout = setTimeout(function() {
console.log('File not changed. Giving up.');
watcher.removeListener('change', onChange);
res.end(
'Unchanged! <a href="?after=' + after[1] + '">Poll again</a>');
}, 10000);
});
最後的「防止服務器被溢出來自同一客戶的多個請求?「 - 你沒有。不是如果你想保證這一點,並仍然允許匿名請求。您可以嘗試基於cookie的排除,但是如果您的服務允許匿名請求,則用戶可以停止發送cookie,在這一點上很難識別來自同一瀏覽器的請求。
eventEmitter方法真的很好用!在客戶端,我開始使用sessionStorage來記錄從服務器返回的時間戳(可能不是最好的方法?)。當服務器以JSON發送新的時間戳時,客戶端將其存儲在sessionStorage中,然後轉向,並使用該值作爲'after'參數進行請求。我已經嘗試過,至少有兩個客戶到目前爲止,它似乎很好!我的下一件事將是有多個文件被觀看;非常感興趣地聽到你如何解決這個問題? – mlehmeher 2015-04-06 14:58:22
我仍然保持一個觀察者。只需將事件中的文件路徑作爲參數附加即可。如果事先知道的文件數量有限,我會在服務器啓動時將這些fs.watchFiles排隊。但是,如果你有很多不同的文件,客戶端可能會隨機觀看,我會把它們全部包裝在一個整潔的類中,以便跟蹤「開放」的觀察者,並在沒有人對它感興趣時解開文件。 – 2015-04-09 17:41:46
所以這個項目結果非常好,這要歸功於你的初始投入。我的服務器現在處理可能正在觀看多個文件的多個客戶端。客戶端請求文件觀看。維護一個隊列,跟蹤每個客戶端請求的服務器分配的GUID。根據您的建議,它們都包裝在一個漂亮整潔的課堂中。 watcher類中的所有事件都使用EventEmitter,所以沒有醜陋的回調嵌套。謝謝您的幫助! – mlehmeher 2015-04-24 21:54:19