使用节点进行多客户端长轮询处理

时间:2015-04-05 22:25:30

标签: node.js long-polling

我正在尝试创建一个节点服务器,它将在服务器上更新文件时通知长轮询客户端。但是,我似乎无法提出代码让服务器识别文件中的更改并更新任何已执行更新的轮询客户端。

我的混淆源于时间戳的处理,特别是我应该跟踪的时间戳。客户只想知道文件是否已更改。我认为我们不应该关心请求何时进入,即我们不必存储请求的时间戳以确定对文件的更改。但是,我们需要跟踪以确定何时使请求到期。

所以,我认为服务器收到请求并立即存储目标文件的时间戳。然后,服务器每隔10秒检查存储的时间戳与当前时间文件的时间戳。如果存在差异,则文件已更新,服务器向客户端发送响应,指示文件已更改。如果服务器在60秒之后没有看到文件中的更改,它会向客户端发送响应以指示客户端应该发起新请求。

上述策略是否有意义?如何有效地处理时间戳的东西?而且,如何同时处理多个客户端?而且,如何防止服务器被来自同一客户端的多个请求溢出?

1 个答案:

答案 0 :(得分: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 );
});

最后&#34;阻止服务器被来自同一客户端的多个请求溢出?&#34; - 你没有。如果您想保证这一点并且仍然允许匿名请求,则不是。您可以尝试基于cookie的排除,但如果您的服务允许匿名请求,则用户可以停止发送cookie,此时很难识别来自同一浏览器的请求。