当前位置: 首页 > 后端技术 > Node.js

简单理解背压机制

时间:2023-04-03 12:40:09 Node.js

前言本文仅代表作者个人观点,如有错误还望指正为什么会有背压机制?我们先来看一段代码。这段代码有什么问题?乍一看没什么问题,但是如果writable.write()写数据慢,但是可读流却在不停地传输数据,就会造成内存溢出和阻塞。constfs=require('fs')constreadable=fs.createReadStream('./LittleWomen.mp4')constwritable=fs.createWriteStream('./LittleWomen(1).mp4')readable.on('data',(chunk)=>{//这里有问题↓↓↓↓↓↓↓writable.write(chunk);})readable.on('end',()=>{writable.end()})流错误处理如果可写流不能正确处理可读流传输的大量数据,那么可读流不会被销毁,从而导致我们写入的文件损坏。我们必须添加适当的错误处理程序以在流失败时销毁管道中的所有流。constgzip=require('zlib').createGzip();constfs=require('fs');constreadable=fs.createReadStream('好莱坞往事.1080p.mkv');constwritable=fs.createWriteStream('HollywoodPastevents.1080p.mkv.gz');//如果可写流失败,压缩文件将无法压缩readable.pipe(gzip).pipe(writable);在Node8.x版本之前我们使用泵。对于更高版本的Node,可以使用管道。constgzip=require('zlib').createGzip();const{pipeline}=require('stream')constfs=require('fs');constreadable=fs.createReadStream('好莱坞往事.1080p.mkv');constwritable=fs.createWriteStream('好莱坞往事.1080p.mkv.gz');管道(可读,gzip,writable,error=>{if(error){console.log('Moviecompressionfailed')}else{console.log('moviecompressedsuccessful')}})我们也可以使用promisify将其转化为async/await形式.constgzip=require('zlib').createGzip()const{pipeline}=require('stream')const{promisify}=require('util')constfs=require('fs')constreadable=fs.createReadStream('好莱坞往事.1080p.mkv')constwritable=fs.createWriteStream('好莱坞往事.1080p.mkv.gz')constasyncPipeline=promisify(pipeline)asyncfunctionstart(){try{awaitasyncPipeline(readable,gzip,writable)console.log('电影压缩成功')}catch(error){console.log('电影压缩失败')}}start()可读流太快而写硬盘的速度远小于硬盘的读取速度。如果可读流速度太快,可写流不能快速消费可读流传输的数据,写流就会pushchunk,推到writequeue中供以后使用,这会导致数据在内存中堆积。这时候会触发backpressur(背压)机制。如果没有backpressur(背压)机制,系统会出现以下问题:内存溢出其他进程会变慢垃圾收集器会过载背压机制是如何解决这些问题的?当在代码中调用管道时,它会向可写流发出数据已准备好传输的信号。当我们的可写流使用write()写入数据时,如果写队列繁忙,或者内部缓冲区已经溢出,write()就会返回false。这时,背压机制会启动,它会暂停任何传入可写流的数据,并等待可写流准备好,清除内部缓冲区。drain事件将被发出,可读流的流式传输将恢复。这意味着管道只会使用固定大小的内存,不会有内存泄漏。为什么我们很少关注背压的问题呢?那是因为Node.js会在您调用管道时自动处理该问题。但是如果我们需要实现自定义流,就需要考虑这些问题。请参阅流中的背压