Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 25.02.2019, 15:24
Новичок на форуме
Отправить личное сообщение для AleRtx Посмотреть профиль Найти все сообщения от AleRtx
 
Регистрация: 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) внутри этой функции-обработчика передадим в другую функцию, то там в темп-файл не пишется ничего. Его размер остаётся нулевым.
Причём, так было три дня подряд. Затем внезапно заработало (без изменений в коде) и работало два дня.
А затем снова перестало.

Приветствуем любые идеи, почему это может происходить :-)
Банальные вещи, типа исчерпания места на диске, проблем с правами доступа на запись и т.п. не предлагать - проверено много раз)
Ответить с цитированием
  #2 (permalink)  
Старый 25.02.2019, 15:44
Профессор
Отправить личное сообщение для Nexus Посмотреть профиль Найти все сообщения от Nexus
 
Регистрация: 04.12.2012
Сообщений: 3,791

Стоит, наверное, описать также, что делает функция, которой передаете request.

Возможно функция, которой передаете request асинхронная и на момент того, как эта функция будет что-то писать в файл нужных данных уже в request'е нет или запись в файл уже окончена.
С nodejs не работаю, это только мое предположение.
Ответить с цитированием
  #3 (permalink)  
Старый 25.02.2019, 15:59
Новичок на форуме
Отправить личное сообщение для AleRtx Посмотреть профиль Найти все сообщения от AleRtx
 
Регистрация: 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.
Ответить с цитированием
  #4 (permalink)  
Старый 28.02.2019, 08:38
Профессор
Отправить личное сообщение для Audaxviator Посмотреть профиль Найти все сообщения от Audaxviator
 
Регистрация: 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.
Ответить с цитированием
  #5 (permalink)  
Старый 28.02.2019, 14:38
Новичок на форуме
Отправить личное сообщение для AleRtx Посмотреть профиль Найти все сообщения от AleRtx
 
Регистрация: 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();
        });

При таком подходе мы закрываем поток ответа всегда гарантированно после того, как мы уже поработали с потоком запроса, всё записали куда нужно и вызвали коллбэк.
Такая последовательность работает без сбоев. Проверено парой сотен экспериментов)
Ответить с цитированием
  #6 (permalink)  
Старый 28.02.2019, 15:13
Профессор
Отправить личное сообщение для Audaxviator Посмотреть профиль Найти все сообщения от Audaxviator
 
Регистрация: 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');
	});
}

А если её вынести в отдельный модуль (не меняя ни буквы) - не работает.
Ну да шут с ней.
Ответить с цитированием
  #7 (permalink)  
Старый 28.02.2019, 15:33
Профессор
Отправить личное сообщение для Audaxviator Посмотреть профиль Найти все сообщения от Audaxviator
 
Регистрация: 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.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Смена background при нажатии JIeuTo Общие вопросы Javascript 5 14.09.2018 18:48
Проблема с радио кнопками px379 Общие вопросы Javascript 8 29.07.2013 09:30
Проблема с дизайном после отправки xmlhttprequest, Проблема с дизайном после отправки cyberx AJAX и COMET 3 01.05.2010 17:07
Проблема с символами Tinvul AJAX и COMET 2 14.07.2009 20:07
Проблема, в менюшке Большой джо Элементы интерфейса 0 12.07.2009 17:12