2015-04-05 60 views
0

我想創建一個節點服務器,當服務器上的文件更新時將通知長輪詢客戶端。但是,我似乎無法拿出代碼讓服務器識別文件中的更改,並更新任何已執行更新的輪詢客戶端。多客戶端長輪詢處理與節點

我的困惑根源在於處理時間戳,具體來說,我應該跟蹤哪些時間戳。客戶只是想知道文件是否已經改變。我認爲我們不應該關心請求何時進入,即我們不需要存儲請求的時間戳以確定對文件的更改。但是,我們需要跟蹤以確定何時到期請求。

所以,我想服務器收到一個請求,並立即存儲目標文件的時間戳。然後,服務器每10秒鐘檢查一次存儲的時間戳與當前時間的文件時間戳。如果存在差異,則文件已更新,並且服務器向客戶端發送指示文件已更改的響應。如果服務器在60秒之後沒有看到文件中的更改,它會向客戶端發送響應,以指示客戶端應該發起新的請求。

上述策略是否有意義?人們如何高效地處理時間戳記?而且,如何同時處理多個客戶?而且,如何防止服務器被來自同一客戶端的多個請求所超時?

回答

1

您需要小心客戶端啓動新請求時發生的情況,因爲文件在此時間段內可能會更改。爲了照顧這個

一種方式是客戶端首先查詢當前文件狀態:

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,在這一點上很難識別來自同一瀏覽器的請求。

+0

eventEmitter方法真的很好用!在客戶端,我開始使用sessionStorage來記錄從服務器返回的時間戳(可能不是最好的方法?)。當服務器以JSON發送新的時間戳時,客戶端將其存儲在sessionStorage中,然後轉向,並使用該值作爲'after'參數進行請求。我已經嘗試過,至少有兩個客戶到目前爲止,它似乎很好!我的下一件事將是有多個文件被觀看;非常感興趣地聽到你如何解決這個問題? – mlehmeher 2015-04-06 14:58:22

+0

我仍然保持一個觀察者。只需將事件中的文件路徑作爲參數附加即可。如果事先知道的文件數量有限,我會在服務器啓動時將這些fs.watchFiles排隊。但是,如果你有很多不同的文件,客戶端可能會隨機觀看,我會把它們全部包裝在一個整潔的類中,以便跟蹤「開放」的觀察者,並在沒有人對它感興趣時解開文件。 – 2015-04-09 17:41:46

+0

所以這個項目結果非常好,這要歸功於你的初始投入。我的服務器現在處理可能正在觀看多個文件的多個客戶端。客戶端請求文件觀看。維護一個隊列,跟蹤每個客戶端請求的服務器分配的GUID。根據您的建議,它們都包裝在一個漂亮整潔的課堂中。 watcher類中的所有事件都使用EventEmitter,所以沒有醜陋的回調嵌套。謝謝您的幫助! – mlehmeher 2015-04-24 21:54:19