面试题答案
一键面试背压产生的原因
在 Node.js 流操作中,背压通常产生于可读流(Readable Stream)产生数据的速度快于可写流(Writable Stream)消费数据的速度。例如,当一个可读流从文件系统快速读取数据,而对应的可写流是向网络套接字写入数据,由于网络传输速度相对较慢,可写流处理数据的速度跟不上可读流产生数据的速度,此时就会出现背压。
Node.js 中处理背压的机制
pipe()
方法:Node.js 的stream
模块中的pipe()
方法会自动处理背压。当使用readable.pipe(writable)
时,可读流会根据可写流的highWaterMark
(缓冲区水位标记)来暂停和恢复数据的读取。如果可写流的内部缓冲区已满(达到highWaterMark
),可读流会暂停读取数据,直到可写流处理完部分数据,触发drain
事件,此时可读流再恢复读取。- 手动处理:通过监听可读流的
data
事件和可写流的drain
事件来手动处理背压。在data
事件中,当写入可写流的数据返回false
时,说明可写流缓冲区已满,此时需要暂停可读流;当可写流触发drain
事件时,说明其缓冲区有空间了,可以恢复可读流。
实际应用场景及处理方式
场景:从文件读取大量数据并通过网络发送出去,比如文件上传功能。假设要将一个大文件上传到远程服务器。 处理方式:
const fs = require('fs');
const http = require('http');
const server = http.createServer((req, res) => {
const readableStream = fs.createReadStream('largeFile.txt');
const writableStream = res;
let paused = false;
readableStream.on('data', (chunk) => {
if (paused) {
// 如果因为背压暂停了,将数据缓存到内部缓冲区
return;
}
const writeResult = writableStream.write(chunk);
if (!writeResult) {
paused = true;
readableStream.pause();
}
});
writableStream.on('drain', () => {
paused = false;
readableStream.resume();
});
readableStream.on('end', () => {
writableStream.end();
});
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
在上述代码中,从文件读取数据(fs.createReadStream
)作为可读流,HTTP 响应(res
)作为可写流。通过监听 data
事件和 drain
事件来处理背压,确保文件数据能平稳地通过网络发送出去,不会因为网络传输速度慢导致内存溢出等问题。