25.02.2019, 15:24
|
Новичок на форуме
|
|
Регистрация: 25.02.2019
Сообщений: 3
|
|
Плавающая проблема с потоком request
Здравствуйте, уважаемые форумчане.
Нужна помощь.
Разрабатываем проект на node.js.
На фронте nginx. Запросы, адресованные на определённую директорию, директит на порт, на котором висят воркеры node.js (в процессе разработки кластером управляет пакет cluster).
В самом worker'е вкратце: const server = http.createServer(); server.on("request",function(req,res){//...});
Суть проблемы заключается в том, что если мы отлавливаем файл, который нам передаётся с фронта прямо вот в этой функции-обработчике события request, то он (поток) нормально перенаправляется в темп-файл (используется пакет tmp).
Условно говоря req.pipe(outStream), где outStream - открытый на запись в режиме потока только что созданный темп-файл, работает прекрасно.
Если же мы реквест (переменная req) внутри этой функции-обработчика передадим в другую функцию, то там в темп-файл не пишется ничего. Его размер остаётся нулевым.
Причём, так было три дня подряд. Затем внезапно заработало (без изменений в коде) и работало два дня.
А затем снова перестало.
Приветствуем любые идеи, почему это может происходить :-)
Банальные вещи, типа исчерпания места на диске, проблем с правами доступа на запись и т.п. не предлагать - проверено много раз)
|
|
25.02.2019, 15:44
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,795
|
|
Стоит, наверное, описать также, что делает функция, которой передаете request.
Возможно функция, которой передаете request асинхронная и на момент того, как эта функция будет что-то писать в файл нужных данных уже в request'е нет или запись в файл уже окончена.
С nodejs не работаю, это только мое предположение.
|
|
25.02.2019, 15:59
|
Новичок на форуме
|
|
Регистрация: 25.02.2019
Сообщений: 3
|
|
Нет, вызываемая не асинхронна.
Попробую кратко процитировать код.
worker.js (так работает)
server.on('request', function (req, res) {
tmp.file({keep: true}, function _tempFileCreated(err, path, fd, cleanupCallback) {
console.log(path);
console.log(fd);
console.log(err);
let oS = fs.createWriteStream(null, {fd: fd});
//let oS = fs.createWriteStream(path);
req.pipe(oS);
});
return;
});
А если worker.js
const api = require('./api');
server.on('request', function (req, res) {
let api_return = api(requestedApiPath, req, config);
});
И при этом внутри api.js
module.exports = (requestedApiPath, req, cfg) => {
const tmp = require('tmp');
const fs = require('fs');
tmp.file({keep: true}, function _tempFileCreated(err, path, fd, cleanupCallback) {
console.log(path);
console.log(fd);
console.log(err);
let oS = fs.createWriteStream(null, {fd: fd});
//let oS = fs.createWriteStream(path);
req.pipe(oS);
});
return;
});
то так уже не работает.
Повторюсь - темп-файл создаётся, но его размер 0.
|
|
28.02.2019, 08:38
|
Профессор
|
|
Регистрация: 28.04.2017
Сообщений: 214
|
|
В общем, тут какая-то заковырка, и может быть, кто-то о ней знает и в состоянии пояснить.
Заковырка в том, что если написать просто функцию
function api(req, res) {
// ... и т.д.
}
положить её рядом и вызвать в теле обработчика, то всё работает правильно.
Файл *.tmp не пишется - только когда её, просто, выносишь в отдельный файл. Т.е. это ведь должно быть пофигу, просто таки, в силу "философии" платформы.
То ли тут какая-то лажа в самом модуле tmp? Вот хотелось бы знать - какая (на будущее).
Там внутри просто функции, которые экспортируются так
module.exports.file = file;
module.exports.dir = dir;
...
Я пробовал, нопремер, изменить на exports.file = file; - но не помогает.
Последний раз редактировалось Audaxviator, 28.02.2019 в 08:53.
|
|
28.02.2019, 14:38
|
Новичок на форуме
|
|
Регистрация: 25.02.2019
Сообщений: 3
|
|
Мы разобрались с источником проблемы. В обработчике события request сервера тмп сохранялся нормально по той причине, что после его сохранения мы писали волшебное слово return. А писали мы его затем, чтобы оставшаяся часть обработчика не исполнялась.
Т.е. чтобы не происходила попытка ещё раз сохранить входной поток.
Ломала же всё команда res.end(), которая следовала далее по тексту.
Комментирование её приводило к тому, что всё нормально сохранялось, независимо от глубины вложенности функций - обработчиков.
Это привело нас к гипотезе о том, что закрытие потока ответа автоматически закрывает (а возможно и уничтожает - не проверяли) и поток запроса. С одной стороны - это логично. С другой - в документации мы такой информации не нашли.
Решением проблемы стала замена вызова в синхронном стиле:
let api_return = api(requestedApiPath, req, config);
На вызов в асинхронном стиле - с коллбэком:
api(requestedApiPath, req, config, function(api_return){
logger.log("api returned result:");
logger.log(api_return);
if (!api_return || !api_return.json || !api_return.statusCode) {
res.writeHead(config.api.errorCommon.statusCode, {'Content-Type': config.contentType});
res.writeJSON(config.api.errorCommon.json);
} else {
res.writeHead(api_return.statusCode, {'Content-Type': config.contentType});
res.writeJSON(api_return.json);
}
res.end();
});
При таком подходе мы закрываем поток ответа всегда гарантированно после того, как мы уже поработали с потоком запроса, всё записали куда нужно и вызвали коллбэк.
Такая последовательность работает без сбоев. Проверено парой сотен экспериментов)
|
|
28.02.2019, 15:13
|
Профессор
|
|
Регистрация: 28.04.2017
Сообщений: 214
|
|
Это не объясняет проблему - в модуле что-то не так.
res.end() ничему не мешает, если его поставить в правильное место. Вот эта функция, положенная рядом, работает правильно
function api(req, res) {
tmp.file({keep: true}, function (err, path, fd, cleanupCallback) {
console.log(path);
console.log(fd);
console.log(err);
var oS = fs.createWriteStream(null, {fd: fd});
req.pipe(oS);
});
req.on('end', function() {
res.end('OK');
});
}
А если её вынести в отдельный модуль (не меняя ни буквы) - не работает.
Ну да шут с ней.
|
|
28.02.2019, 15:33
|
Профессор
|
|
Регистрация: 28.04.2017
Сообщений: 214
|
|
Ха! Она заработала, когда я её выкинул в отдельный модуль - именно "не меняя ни буквы", и экспортировал так (на манер Lisp, ага)
const tmp = require('tmp'),
fs = require('fs');
function api(req, res) {
tmp.file({keep: true}, function (err, path, fd, cleanupCallback) {
console.log(path);
console.log(fd);
console.log(err);
var oS = fs.createWriteStream(null, {fd: fd});
req.pipe(oS);
});
req.on('end', function() {
res.end('OK');
});
};
module.exports = api;
Последний раз редактировалось Audaxviator, 28.02.2019 в 16:44.
|
|
|
|