面试题答案
一键面试合理使用可读流和可写流
- 可读流(Readable Stream):用于从大文件中按块读取数据,而不是一次性将整个文件读入内存。通过设置适当的
highWaterMark
(缓冲区大小),控制每次读取的数据量,避免内存占用过高。当数据可读时,触发data
事件,将数据块传递给可写流。 - 可写流(Writable Stream):负责将从可读流接收到的数据块写入网络连接(例如
http.ServerResponse
或网络套接字)。可写流也有highWaterMark
属性,当写入缓冲区已满时,需要暂停可读流,防止数据丢失。当可写流缓冲区有空间时,触发drain
事件,此时可以恢复可读流。
关键代码示例
以下是一个简单的示例,使用 fs.createReadStream
创建可读流,http.ServerResponse
作为可写流来实现大文件的网络传输:
const http = require('http');
const fs = require('fs');
const server = http.createServer((req, res) => {
const readableStream = fs.createReadStream('largeFile.txt');
readableStream.on('data', (chunk) => {
// 如果可写流缓冲区已满,暂停可读流
if (res.write(chunk) === false) {
readableStream.pause();
}
});
readableStream.on('end', () => {
res.end();
});
res.on('drain', () => {
// 可写流缓冲区有空间,恢复可读流
readableStream.resume();
});
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
提升网络通信性能的原因
- 减少内存占用:通过按块读取和写入数据,避免一次性将整个大文件加载到内存中,降低了内存使用量,使系统能够处理更大的文件而不会因内存不足崩溃。
- 流量控制:利用可写流的
drain
事件和可读流的pause
与resume
方法,实现了流量控制。当网络传输速度较慢,可写流缓冲区满时,暂停可读流,防止数据堆积在内存中;当缓冲区有空间时,恢复可读流,保证数据持续传输,提高了网络通信的稳定性和效率。 - 异步处理:流的操作是基于事件驱动的异步操作,不会阻塞主线程,使得 Node.js 可以在传输文件的同时处理其他请求,提高了服务器的整体性能和并发处理能力。