共计 1954 个字符,预计需要花费 5 分钟才能阅读完成。
前言
本文仅是作者本人的见解,如有谬误还请即便斧正
为什么存在背压机制?
咱们首先来看一段代码, 这段代码存在什么问题?
乍一看,感觉没啥大故障,然而如果 writable.write()
写入数据比较慢,然而可读流又在一直的传输数据,就会造成内存溢出,造成阻塞。
const fs = require('fs')
const readable = fs.createReadStream('./ 小妇人.mp4')
const writable = fs.createWriteStream('./ 小妇人(1).mp4')
readable.on('data', (chunk) => {
// 这里存在问题↓↓↓↓↓↓↓
writable.write(chunk);
})
readable.on('end', () => {writable.end()
})
流的错误处理
如果可写流,无奈正确的解决大量由可读流传输的数据,可读流并不会被销毁,这会导致咱们写入的文件被损坏。咱们必须增加适当的谬误处理程序,在当流产生故障的时候,销毁管道中的所有流。
const gzip = require('zlib').createGzip();
const fs = require('fs');
const readable = fs.createReadStream('好莱坞往事.1080p.mkv');
const writable = fs.createWriteStream('好莱坞往事.1080p.mkv.gz');
// 如果可写流产生故障,压缩文件会压缩失败
readable.pipe(gzip).pipe(writable);
在 Node 8.x 版本之前咱们应用 pump
。对于更高版本的 Node, 能够应用pipeline
。
const gzip = require('zlib').createGzip();
const {pipeline} = require('stream')
const fs = require('fs');
const readable = fs.createReadStream('好莱坞往事.1080p.mkv');
const writable = fs.createWriteStream('好莱坞往事.1080p.mkv.gz');
pipeline(
readable,
gzip,
writable,
error => {if (error) {console.log('电影压缩失败')
} else {console.log('电影压缩胜利')
}
}
)
咱们也能够应用 promisify
将其革新成 async/await
的模式。
const gzip = require('zlib').createGzip()
const {pipeline} = require('stream')
const {promisify} = require('util')
const fs = require('fs')
const readable = fs.createReadStream('好莱坞往事.1080p.mkv')
const writable = fs.createWriteStream('好莱坞往事.1080p.mkv.gz')
const asyncPipeline = promisify(pipeline)
async function start () {
try {
await asyncPipeline(
readable,
gzip,
writable
)
console.log('电影压缩胜利')
} catch (error) {console.log('电影压缩失败')
}
}
start()
可读流太快了
硬盘的写入速度,远远小于硬盘的读取速度。如果可读流太快,而可写流的无奈迅速的生产可读流传输的数据,写入流将会把 chunk,push 到写队列中不便之后应用,这样就会造成数据在内存中的累积。这个时候将会触发 backpressur(背压) 机制。如果没有 backpressur(背压) 机制,零碎将会呈现如下的问题:
- 内存溢出
- 其余过程变得迟缓
- 垃圾收集器将超负荷运作
背压机制是如何解决这些问题的?
在代码中调用 pipe
时,它会向可写流发出信号,示意有数据筹备传输。当咱们的可写流应用 write()
写入数据时,如果写队列忙碌,或者外部缓存区曾经溢出了,write()
将会返回 false。
这个时候,背压机制就会启动,它会暂停任何数据传入到可写流中,并期待可写流筹备好,清空外部缓存区后。将会收回 drain
事件,并复原可读流的传输。
这就意味着 pipe
只会应用固定大小的内存,不会存在内存透露的问题。
为什么咱们平时很少关注背压的问题呢?那是因为在你调用 pipe
时,Node.js 曾经主动解决了这些问题。然而如果咱们须要实现自定义流,则须要思考到这些问题。
参考
- Backpressuring in Streams