2013-02-11 99 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