Простое понимание механизма противодавления

Node.js внешний интерфейс
Простое понимание механизма противодавления

предисловие

Эта статья является лишь собственным мнением автора, если есть какие-либо ошибки, пожалуйста, поправьте меня.

Почему существует механизм обратного давления?

Давайте сначала посмотрим на кусок кода, что не так с этим кодом?

На первый взгляд кажется, что ничего страшного, но если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()

Читаемый поток слишком быстрый

Скорость записи жесткого диска намного меньше, чем скорость чтения жесткого диска. Если читаемый поток слишком быстрый, а записываемый поток не может быстро потреблять данные, переданные читаемым потоком, поток записи разбивается на фрагменты и помещается в очередь записи для последующего использования, что приведет к накоплению данных в памяти. Это вызоветпротиводавлениемеханизм. если нетпротиводавлениемеханизма, система будет иметь следующие проблемы:

  1. переполнение памяти
  2. другие процессы замедляются
  3. Сборщик мусора будет перегружен

Как механизм противодавления решает эти проблемы?

звонить по кодуpipe, он сообщает доступному для записи потоку, что данные готовы к передаче. Когда наш доступный для записи поток используетwrite()При записи данных, если очередь записи занята или внутренний буфер переполнен,write()вернет ложь.

В это время запустится механизм обратного давления, он приостановит передачу любых данных в доступный для записи поток и будет ждать готовности записываемого потока после очистки внутреннего буфера. будет изданоdrainсобытие и возобновить передачу читаемого потока.

это значитpipeБудет использоваться только фиксированный размер памяти, утечек памяти не будет.

Почему мы обычно мало внимания уделяем проблеме обратного давления? Это потому, что когда вы звонитеpipeчас,Node.js уже автоматически обрабатывает эти проблемы.. Но если нам нужно реализовать пользовательские потоки, мы должны принять во внимание эти проблемы.

Ссылаться на