Обработка ошибок с помощью потоков node.js

Каков правильный способ обработки ошибок с потоками? Я уже знаю, что есть событие "ошибки", которое вы можете прослушать, но я хочу узнать более подробную информацию о произвольно сложных ситуациях.

Для начала, что вы делаете, когда хотите сделать простую цепочку труб:

input.pipe(transformA).pipe(transformB).pipe(transformC)...

И как вы правильно создаете одно из этих преобразований, чтобы ошибки обрабатывались правильно?

Другие связанные вопросы:

  • когда происходит ошибка, что происходит с событием "end"? Это никогда не срабатывает? Иногда его увольняют? Это зависит от преобразования/потока? Каковы стандарты здесь?
  • Существуют ли какие-либо механизмы для ошибок распространения через трубы?
  • Домены эффективно решают эту проблему? Примеры были бы приятными.
  • Ошибки, возникающие из событий "error", имеют трассировки стека? Иногда? Никогда? есть ли способ получить их от них?
+137
14 февр. '14 в 5:02
источник поделиться
7 ответов

преобразование

Потоки преобразования являются как читаемыми, так и записываемыми, и поэтому являются действительно хорошими "средними" потоками. По этой причине их иногда называют through потоки. В этом смысле они похожи на дуплексные потоки, за исключением того, что предоставляют приятный интерфейс для манипулирования данными, а не просто для их отправки. Цель потока преобразования - манипулировать данными, когда они передаются по потоку. Вы можете, например, сделать несколько асинхронных вызовов или получить пару полей, переназначить некоторые вещи и т.д.


Where you might put a transform stream


Как создать поток преобразования смотрите здесь и здесь. Все, что вам нужно сделать, это:

  1. включить модуль потока
  2. создавать (или наследовать) класс Transform
  3. реализовать метод _transform который принимает (chunk, encoding, callback).

Кусок - это ваши данные. Большую часть времени вам не нужно беспокоиться о кодировке, если вы работаете в objectMode = true. Обратный вызов вызывается, когда вы закончите обработку чанка. Этот кусок затем передается следующему потоку.

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

Для обработки ошибок, продолжайте читать.

труба

В цепочке трубопроводов обработка ошибок действительно нетривиальна. В соответствии с этим потоком.pipe() не создан для пересылки ошибок. Так что-то вроде...

var a = createStream();
a.pipe(b).pipe(c).on('error', function(e){handleError(e)});

... будет только слушать ошибки в потоке c. Если событие ошибки было отправлено на a, то оно не будет передано и, фактически, сгенерирует. Чтобы сделать это правильно:

var a = createStream();
a.on('error', function(e){handleError(e)})
.pipe(b)
.on('error', function(e){handleError(e)})
.pipe(c)
.on('error', function(e){handleError(e)});

Теперь, хотя второй способ более многословен, вы можете, по крайней мере, сохранить контекст, в котором происходят ваши ошибки. Обычно это хорошая вещь.

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

конец

При возникновении события ошибки конечное событие не будет запущено (явно). Излучение события ошибки завершит поток.

домены

По моему опыту, домены работают очень хорошо большую часть времени. Если у вас есть необработанное событие ошибки (т.е. выдается ошибка в потоке без прослушивателя), сервер может дать сбой. Теперь, как указано в приведенной выше статье, вы можете заключить поток в домен, который должен правильно отлавливать все ошибки.

var d = domain.create();
 d.on('error', handleAllErrors);
 d.run(function() {
     fs.createReadStream(tarball)
       .pipe(gzip.Gunzip())
       .pipe(tar.Extract({ path: targetPath }))
       .on('close', cb);
 });

Прелесть доменов в том, что они сохранят следы стека. Хотя Event-Stream также хорошо справляется с этой задачей.

Для получения дополнительной информации ознакомьтесь со справочником. Довольно подробно, но очень полезно и дает отличные ссылки на множество полезных модулей.

+190
13 мар. '14 в 19:55
источник

Связанные вопросы


Похожие вопросы

Домены

устарели. вам они не нужны.

для этого вопроса различия между трансформируемым или записываемым не так важны.

mshell_lauren ответ велик, но в качестве альтернативы вы также можете явно прослушивать событие ошибки в каждом потоке, который, по вашему мнению, может быть ошибкой. и повторное использование функции обработчика, если вы предпочитаете.

var a = createReadableStream()
var b = anotherTypeOfStream()
var c = createWriteStream()

a.on('error', handler)
b.on('error', handler)
c.on('error', handler)

a.pipe(b).pipe(c)

function handler (err) { console.log(err) }

это предотвращает печально известное неперехваченное исключение, если один из этих потоков запускает свое событие ошибки

+22
29 дек. '15 в 10:14
источник

Если вы используете узел> = v10.0.0, вы можете использовать stream.pipeline и stream.finished.

Например:

const { pipeline, finished } = require('stream');

pipeline(
  input, 
  transformA, 
  transformB, 
  transformC, 
  (err) => {
    if (err) {
      console.error('Pipeline failed', err);
    } else {
      console.log('Pipeline succeeded');
    }
});


finished(input, (err) => {
  if (err) {
    console.error('Stream failed', err);
  } else {
    console.log('Stream is done reading');
  }
});

Смотрите этот github PR для дальнейшего обсуждения.

+9
12 нояб. '18 в 11:33
источник

Ошибки всей цепочки могут быть переданы в самый правый поток с помощью простой функции:

function safePipe (readable, transforms) {
    while (transforms.length > 0) {
        var new_readable = transforms.shift();
        readable.on("error", function(e) { new_readable.emit("error", e); });
        readable.pipe(new_readable);
        readable = new_readable;
    }
    return readable;
}

который можно использовать как:

safePipe(readable, [ transform1, transform2, ... ]);
+6
21 апр. '16 в 14:55
источник

.on("error", handler) заботится только об ошибках потока, но если вы используете пользовательские потоки преобразования, .on("error", handler) не перехватывает ошибки, возникающие внутри функции _transform. Таким образом, можно сделать что-то вроде этого для управления потоком приложений:

this ключевое слово в функции _transform относится к самому Stream, который является EventEmitter. Таким образом, вы можете использовать try catch как try catch ниже, чтобы перехватить ошибки и затем передать их в пользовательские обработчики событий.

// CustomTransform.js
CustomTransformStream.prototype._transform = function (data, enc, done) {
  var stream = this
  try {
    // Do your transform code
  } catch (e) {
    // Now based on the error type, with an if or switch statement
    stream.emit("CTError1", e)
    stream.emit("CTError2", e)
  }
  done()
}

// StreamImplementation.js
someReadStream
  .pipe(CustomTransformStream)
  .on("CTError1", function (e) { console.log(e) })
  .on("CTError2", function (e) { /*Lets do something else*/ })
  .pipe(someWriteStream)

Таким образом, вы можете хранить свою логику и обработчики ошибок отдельно. Кроме того, вы можете обрабатывать только некоторые ошибки и игнорировать другие.

ОБНОВИТЬ
Альтернатива: наблюдаемый RXJS

+3
02 дек. '16 в 9:10
источник

Try catch не будет регистрировать ошибки, которые произошли в потоке, потому что они генерируются после того, как вызывающий код уже завершился. Вы можете обратиться к документации:

https://nodejs.org/dist/latest-v10.x/docs/api/errors.html

0
15 апр. '19 в 2:48
источник

Используйте шаблон Node.js, создав механику потока Transform и вызывая ее обратный вызов done с аргументом для распространения ошибки:

var transformStream1 = new stream.Transform(/*{objectMode: true}*/);

transformStream1.prototype._transform = function (chunk, encoding, done) {
  //var stream = this;

  try {
    // Do your transform code
    /* ... */
  } catch (error) {
    // nodejs style for propagating an error
    return done(error);
  }

  // Here, everything went well
  done();
}

// Let use the transform stream, assuming `someReadStream`
// and `someWriteStream` have been defined before
someReadStream
  .pipe(transformStream1)
  .on('error', function (error) {
    console.error('Error in transformStream1:');
    console.error(error);
    process.exit(-1);
   })
  .pipe(someWriteStream)
  .on('close', function () {
    console.log('OK.');
    process.exit();
  })
  .on('error', function (error) {
    console.error(error);
    process.exit(-1);
   });
0
27 сент. '17 в 16:09
источник

Посмотрите другие вопросы по меткам или Задайте вопрос