【发布时间】:2019-11-17 23:31:19
【问题描述】:
这是一个很难描述的问题。
我有一个koajs 应用程序,该应用程序具有每 2 分钟在多个实例(10-1000 范围)中创建的功能。此计划作业在应用程序启动时创建。我使用koajs,因为我需要一些简单的 api 端点用于这个应用程序。前 3-5 小时运行良好,然后创建的实例数开始减少,部分日志输出消失。
这是基于实际代码的最小示例:
server.ts
const bootstrap = async () => {
process.setMaxListeners(0); //(node:7310) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 uncaughtException listeners added to [process]. Use emitter.setMaxListeners() to increase limit
//appears on app startup (however seems like this setMaxListeners(0) doesnt affect anything since the warning persist)
const app = new Koa();
app.use(async ctx => {
ctx.body = "Welcome to my Server!";
});
app.listen(port);
new Main().run();
};
bootstrap();
main.ts(尝试:cron npm 包,node-scheduler,setInterval,递归setTimeout)运行scheduledJobWrapper。
isStarting: boolean = false;
async run() {
logger.info(`running the app, every 2 minutes`);
//let that = this;
// new CronJob(`*/2 * * * *`, function () {
// that.scheduledJobWrapper();
// }, null, true, 'America/Los_Angeles');
const interval = 2 * 60 * 1000;
setInterval(() => {
this.scheduledJobWrapper();
}, interval);
}
async scheduledJobWrapper() {
logger.info("here scheduledJobWrapper");
let args = {};
//some irrelevant logic to set the arguments
await this.scheduledJob(args);
}
async scheduledJob(args) {
try {
logger.info("starting");
if (!this.isStarting) {
this.isStarting = true;
const runningCount = Executor.tasks.length; //Executor.tasks is a singleton containing some info about tasks. details are irrelevant. the point is it contains the active tasks.
const tasksLimit = 100;
if (runningCount < tasksLimit) {
for await (const i of Array(tasksLimit - runningCount).keys()) {
if (Executor.tasks.length > 20)
await global.sleep(5 * 1000);
this.startWrapper(args); //calling main task here
}
}
this.isStarting = false;
logger.info(`Started: ${Executor.tasks.length - runningCount}`);
}
} catch (e) {
logger.error("Error running scheduled job: " + e.toString());
}
}
在本例中,问题表现如下: 前 3-5 小时所有工作都按预期工作,之后每次调用计划的函数:
-
logger.info("here scheduledJobWrapper");现在会显示任何输出。 -
logger.info("starting");不在输出中 -
this.startWrapper确实运行并且其中的代码正在执行。
尽管this.startWrapper 内部的代码仍在运行,但新创建的作业数量正在缓慢减少。
硬件 (RAM/CPU) 没有得到任何显着负载(CPU 低于 10%,RAM 低于 20%)
关于可能原因的任何线索?
nodejs: 12.6.0
谢谢!
更新
似乎使用setInterval 后,应用程序可以正常运行更长的时间(6-24 小时),但之后问题仍然存在。
【问题讨论】:
-
可能不是问题,但我建议不要使用
arguments作为变量名,因为标识符是在所有 JS 函数中预定义的。 more info -
不是实际的变量名,只是举例
-
我建议在
await global.sleep(5 * 1000);之前加上logger.info声明。用它来打印Executor.tasks.length的值。 -
我认为这是一个泄漏的异步/等待逻辑问题,而不是直接 nodejs 限制。不幸的是,它的代码被破坏了,很难说。一个工作示例会更好,因为 async/await 存在问题。 1) 为什么使用
for await来迭代一个简单的数组? 2) 您读取了三个可能不同的Executor.tasks.length值,具体取决于任务数组何时相对于检查被修改,所以 3) 为什么假设 Executor.tasks 管理不是问题的一部分?您为 20 多个任务添加 5s 延迟,最大值为 100,这保证批次超过 2m setInterval 间隔,使其可重入。 -
for await不是这样工作的。
标签: javascript node.js typescript