2013-02-11 96 views
2

是否可以写非阻塞response.write?我写了一个简单的测试,看看其他客户端可以连接,而一个下载文件:http.ServerResponse.write()是否阻塞?

var connect = require('connect'); 

var longString = 'a'; 
for (var i = 0; i < 29; i++) { // 512 MiB 
    longString += longString; 
} 
console.log(longString.length) 

function download(request, response) { 
    response.setHeader("Content-Length", longString.length); 
    response.setHeader("Content-Type", "application/force-download"); 
    response.setHeader("Content-Disposition", 'attachment; filename="file"'); 
    response.write(longString); 
    response.end(); 
} 

var app = connect().use(download); 
connect.createServer(app).listen(80); 

而现在似乎write阻止!

我做错了什么?

更新因此,它不会阻止,并在同一时间阻止。它并不妨碍两个文件可以同时下载。而且它阻止了创建缓冲区是一个漫长的操作。

回答

1

不,它不阻止,我尝试了一个从IE和Firefox等。我先做了IE,但仍然可以先从Firefox下载文件。 我尝试了1 MB(我< 20)它更快地工作。 您应该知道,无论您创建哪种longString,都需要分配内存。尝试做我的< 30(在Windows 7上),它会抛出致命错误:JS分配失败 - 进程内存不足。

内存分配/复制需要花费时间。由于它是一个巨大的文件,所以需要花费时间并且您的下载看起来像阻塞。尝试一下自己的较小的值(我< 20或其他东西)

+0

不会创建该字符串只有一次,在服务器启动时? – Vanuan 2013-02-11 19:02:08

+0

是的,但是对每个请求都构建了响应。所以它被复制为每个响应。响应是I/O密集型,同时也考虑到其庞大的规模。 – user568109 2013-02-11 19:12:16

+0

对我而言,1 MB在不到一秒钟内加载完毕,您是如何设法点击这么快的? – Vanuan 2013-02-11 19:30:33

2

任何严格的JavaScript处理将阻止。 response.write(),至少为V0.8的,是no exception这样:

The first time response.write() is called, it will send the buffered header information and the first body to the client. The second time response.write() is called, Node assumes you're going to be streaming data, and sends that separately. That is, the response is buffered up to the first chunk of body.

Returns true if the entire data was flushed successfully to the kernel buffer. Returns false if all or part of the data was queued in user memory. 'drain' will be emitted when the buffer is again free.

什么可以节省一些时间试图write()它之前longString转换为Buffer,因为转换会发生反正:

var longString = 'a'; 
for (...) { ... } 
longString = new Buffer(longString); 

但是,它可能会更好streamlongString各块,而不是全在一次(注:Streams are changing in v0.10):

var longString = 'a', 
    chunkCount = Math.pow(2, 29), 
    bufferSize = Buffer.byteLength(longString), 
    longBuffer = new Buffer(longString); 

function download(request, response) { 
    var current = 0; 

    response.setHeader("Content-Length", bufferSize * chunkCount); 
    response.setHeader("Content-Type", "application/force-download"); 
    response.setHeader("Content-Disposition", 'attachment; filename="file"'); 

    function writeChunk() { 
     if (current < chunkCount) { 
      current++; 

      if (response.write(longBuffer)) { 
       process.nextTick(writeChunk); 
      } else { 
       response.once('drain', writeChunk); 
      } 
     } else { 
      response.end(); 
     } 
    } 

    writeChunk(); 
} 

而且,如果最终目标是从流磁盘上的文件,这可能是更容易与fs.createReadStream()stream.pipe()

function download(request, response) { 
    // response.setHeader(...) 
    // ... 

    fs.createReadStream('./file-on-disk').pipe(response); 
} 
+0

稍作修改:'1 * 29!= 2^29' – Vanuan 2013-02-11 19:38:39

+0

您可以详细说明'process.nextTick'的用途吗?它和setTimeout一样吗? – Vanuan 2013-02-11 19:40:29

+0

@Vanuan更正为'2^29'。而且,我使用了['process.nextTick()'](http://nodejs.org/docs/v0.8.19/api/process.html#process_process_nexttick_callback),因此每个块都被写入事件循环的不同记号中。 – 2013-02-11 19:48:11