Express / Node.js ..我是否阻止事件循环?

时间:2019-05-05 17:40:49

标签: javascript node.js express

我在快速服务器上有一条路由,该路由必须调用外部API,该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 个答案:

答案 0 :(得分:2)

您已达到Node异步I / O的默认限制!长话短说,对于fs模块,Node.js使用 libuv线程池,默认情况下其大小等于4。在某些方面,Node将其工作委托给底层操作系统异步处理程序(epoll,kqueue等),但是对于诸如DNS,crypto或文件系统(例如我们的文件系统)之类的东西,它使用libuv。要写入磁盘的文件数量很可能大于4。当并行请求进入时,文件数量甚至可能变得更大。最终,您将用完libuv线程,然后Node便拥有了无需执行任何操作,而是等待至少有一个线程可用。它实际上取决于文件的数量,因此您的应用程序的行为不稳定。

您可以做的是,您可以通过传递数值大于4的环境变量UV_THREADPOOL_SIZE来增加线程池的大小。但是它仍然非常有限。老实说,Node.js事件循环模型不是此类事情的最佳选择。还要考虑不同请求以相同名称写入文件的情况。如果您对“最后一次写入获胜”并发模型感到满意,那么您可能还可以,但是由于操作顺序错误,文件可能最终被破坏。这是一项艰巨的任务。

有关libuv和这些奇特线程池的更多详细信息,建议您观看这个不错的talk

实际上,Node在fs上的官方docs警告您这种行为。

答案 1 :(得分:0)

j + 1

当然,这个答案不是很好的解决方案,
但是通过简短的工作,我们可以通过 node js global 解决该API的锁定问题。


另一个提示 如果使用相同的文件名写入文件时出现问题,
我们可以用

解决
  1. 写入临时文件名
  2. 重命名temfilename以更正文件名

但是,如果在读取相同文件时遇到问题(第三方API的问题),则锁定会更稳定。


另外,请在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; }) }); }); catch(error=>console.log(error);之间添加
可以发现问题出在哪里