我在快速服务器上有一条路由,该路由必须调用外部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();
})
});
});
答案 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的锁定问题。
另一个提示
如果使用相同的文件名写入文件时出现问题,
我们可以用
但是,如果在读取相同文件时遇到问题(第三方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);
之间添加
可以发现问题出在哪里