Express / Node.js .. Я блокирую цикл обработки событий? - PullRequest
3 голосов
/ 05 мая 2019

У меня есть маршрут на экспресс-сервере, который должен вызвать внешний API, который отправляет обратно список файлов на этом сервере. После этого вы вызываете другой API-интерфейс для получения содержимого каждого файла. Как только у меня есть это, я пишу содержание каждого файла в новый файл в корневом каталоге моего проекта.

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

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

Иногда он вообще не завершается, иногда быстро выдает ошибку обоим пользователям, а иногда только один завершается ошибкой, а другой завершается.

Я осматривался и подозреваю, что блокирую цикл обработки событий и мне нужно использовать что-то вроде рабочих потоков. Мой вопрос, я нахожусь на правильный путь с этим или это что-то еще, что я не знаю?

Код в основном выглядит следующим образом:

//this whole request takes about 3 minutes to complete if successful due to rate limiting of the external APIs.
//it's hard to imagine why I would want to do this kind of thing, but it's not so important.. what is really important
//is why I get issues with more than 1 user hitting the route.
router.get('/api/myroute', (req, res, next) => {

    //contact a remote server's API, it sends back a big list of files.
    REMOTE_SERVER.file_list.list(USER_CREDS.id).then(files => {

        //we need to get the contents of each specific file, so we do that here.
        Promise.all(files.map((item, i) =>
            //they have an API for specific files, but you need the list of those files first like we retrieved above.
            REMOTE_SERVER.specific_file.get(USER_CREDS.id, {
                file: { key: files[i].key }
            }).then(asset => {

                //write the contents of each file to a directory called "my_files" in the project root.
                fs.writeFile('./my_files/' + file.key, file.value, function (err) {
                    if (err) {
                        console.log(err);
                    };
                });
            })))
            .then(() => {
                console.log("DONE!!");
                res.status(200).send();
            })
    });
});

Ответы [ 2 ]

2 голосов
/ 05 мая 2019

Вы выполнили ограничения по умолчанию для асинхронного ввода-вывода Node!Короче говоря, для fs модуля Node.js используется пул потоков libuv , размер которого по умолчанию равен 4.В некоторых случаях Node делегирует свою работу базовым асинхронным обработчикам операционной системы (epoll, kqueue и т. Д.), Но для таких вещей, как DNS, crypto или, в нашем случае, файловой системе, он использует libuv.Скорее всего, количество файлов, которые вы хотите записать на диск, больше 4. Скорее всего, оно становится еще больше, когда поступает параллельный запрос. В конце дня у вас просто заканчиваются потоки libuv, а затем Node просто имеетничего не делать, но подождать, пока хотя бы один поток свободен в использовании.Это действительно зависит от количества файлов, поэтому поведение вашего приложения нестабильно.

Что вы можете сделать, это то, что вы можете увеличить размер пула потоков, передав переменную среды UV_THREADPOOL_SIZE с числовым значениембольше 4. Но это все еще очень ограничено.Если честно, модель цикла событий Node.js - не лучший выбор для таких вещей.Также подумайте о случаях, когда разные запросы записывают файлы с одинаковыми именами.Если вы согласны с моделью параллелизма «последняя запись выигрывает», то, возможно, она вам подходит, но ваши файлы могут быть повреждены из-за неправильного порядка операций.Это довольно трудная задача для решения.

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

На самом деле официальный сайт Node документы на fs предупреждают вас о таком поведении.

0 голосов
/ 05 мая 2019
router.get('/api/myroute', (req, res, next) => {

    //Check this api is processing
    if (global.isLocked_ApiMyroute) {
        res.status(200).send('Please try again after a few minutes');
        return;
    }

    //contact a remote server's API, it sends back a big list of files.

    //lock this api while processing
    global.isLocked_ApiMyroute = true;

    REMOTE_SERVER.file_list.list(USER_CREDS.id).then(files => {

        //we need to get the contents of each specific file, so we do that here.
        Promise.all(  ... )
            .then(() => {
                console.log("DONE!!");
                res.status(200).send();
                global.isLocked_ApiMyroute = false;
            })
            .catch(() => { // added catch block : because of [anycase, isLocked_ApiMyroute must be false]
                global.isLocked_ApiMyroute = false;
            })
    });
});

Конечно, этот ответ не является хорошим решением,
Но с небольшой работой мы можем решить через узел js global для блокировки этого API.


Еще один совет Если есть проблема с записью файла с тем же именем файла,
Мы можем решить с

  1. Написать имя временного файла
  2. переименовать имя_файла в правильное имя файла

Но, если проблема с тем же чтением файла ( Проблема со сторонним API ), блокировка более стабильна.


Также, пожалуйста, добавьте catch(error=>console.log(error); с then
Это может быть найти проблему, откуда

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...