【问题标题】:How to execute / abort long running tasks in Node JS?如何在 Node JS 中执行/中止长时间运行的任务?
【发布时间】:2015-04-27 13:13:21
【问题描述】:

带有 Mongo DB 的 NodeJS 服务器 - 一个功能将从 DB 生成报告 JSON 文件,这可能需要一段时间(60 秒以上 - 必须处理数十万个条目)。

我们希望将其作为后台任务运行。如果用户决定更改参数并重新构建它,我们需要能够启动报表构建过程、监控它并中止它。

节点最简单的方法是什么?不想进入单独的工作服务器处理作业、消息队列等领域 - 我们需要将其保持在同一个盒子上并且实现相当简单。

1) 以异步方法启动构建,并返回给用户,socket.io 报告进度?

2) 为构建脚本分离一个子进程?

3) 使用类似https://www.npmjs.com/package/webworker-threads?

通过我研究过的几种方法,我陷入了相同的两个领域;

1) 如何监控进度? 2) 如果用户重新提交数据,如何中止现有的构建过程?

任何指针将不胜感激......

【问题讨论】:

    标签: node.js multithreading mongodb child-process long-running-processes


    【解决方案1】:

    最好将此任务与您的主应用程序分开。也就是说,在后台运行它很容易。 要在后台运行它并在没有消息队列等的情况下进行监控,最简单的方法是child_process

    1. 您可以在用户调用的端点(或 url)上启动 spawn 作业。
    2. 接下来,设置socket 以返回对子进程的实时监控
    3. 添加另一个端点以停止作业,并使用1. 返回的唯一 ID(或不添加,取决于您的并发需求)

    一些编码思路:

    var spawn = require('child_process').spawn
    
    var job = null //keeping the job in memory to kill it
    
    app.get('/save', function(req, res) {
    
        if(job && job.pid)
            return res.status(500).send('Job is already running').end()
    
        job = spawn('node', ['/path/to/save/job.js'], 
        {
            detached: false, //if not detached and your main process dies, the child will be killed too
            stdio: [process.stdin, process.stdout, process.stderr] //those can be file streams for logs or wathever
        })
    
        job.on('close', function(code) { 
            job = null 
            //send socket informations about the job ending
        })
    
        return res.status(201) //created
    })
    
    app.get('/stop', function(req, res) {
        if(!job || !job.pid)
            return res.status(404).end()
    
        job.kill('SIGTERM')
        //or process.kill(job.pid, 'SIGTERM')
        job = null
        return res.status(200).end()
    })
    
    app.get('/isAlive', function(req, res) {
        try {
            job.kill(0)
            return res.status(200).end()
        } catch(e) { return res.status(500).send(e).end() }
    })
    

    要监控子进程,您可以使用pidusage,例如,我们在PM2 中使用它。添加一条路线来监控作业并每秒调用一次。作业结束时不要忘记释放内存。


    您可能想查看this library,它将帮助您管理跨微服务的多处理。

    【讨论】:

    • 感谢@soyuka 的回答。使用 job var - 保留对子项的引用,因此您可以停止它 - 但它适用于 PID 吗? PID得到重新使用不是吗?所以我们生成的工作可能会完成并且它的 PID 被释放给任何其他新进程来处理?这意味着 job.kill() 如果单独使用 PID 可能会杀死不同的进程?或者它不是那样工作的......
    • 只需检查文档,它会说...“当信号无法传递时,可能会发出'错误'事件。向已经退出的子进程发送信号不是错误,但可能有无法预料的后果:如果 PID(进程 ID)已被重新分配给另一个进程,则信号将被传递给该进程。接下来会发生什么是任何人的猜测。但是根据您的示例,在完成时取消引用应该可以解决这个问题!抱歉,错过了。
    • 此代码似乎一次只允许一个报告作业,如果尝试启动第二个作业,则会覆盖(并丢失之前的作业)。
    • @jfriend00 确实,这就是我谈到并发需求的原因(问题中没有说明)。如果您想要更多作业,只需保留一个 pid-cache 数组来保存子进程。 @MattBryson 实际上,您必须确保在作业结束时删除内存引用(exitclose 事件)。当信号被捕获时,closestdsexit 相关。
    • 如果作业已经在运行,@jfriend00 添加了一个条件。请记住,这是一个小草稿,我想给出一些关于我将如何做到这一点的提示,但我没有做一个完整的工作示例;)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多