在XML解析块期间从socket.io发出,直到处理完成

时间:2015-10-16 15:00:35

标签: node.js express socket.io sax

我有一个带有角度客户端的nodeJS / express / socket.io设置。

从服务器到客户端的消息似乎被阻止,直到长时间运行的进程完成然后一次到达,为什么会这样?

该网站的一个功能是让用户上传xml文件进行处理。文件并非无关紧要,处理大约需要20秒。

我正在使用sax包装器(xml-flow)来解析XML,这会在相关xml标记的末尾引发事件。这些事件调用一个回调,然后调用socket.emit()到客户端来指示进度。

似乎所有内容都可以正常连接,但是进程消息似乎会以某种方式被阻止,直到解析结束,此时它们都会立即到达客户端。

使用断点,我已经确定了socket.emit()调用是否定期进行,我不知道任何批处理机制。

如果有帮助,我很乐意发布您认为相关的任何其他代码。

socket.on('analysis:request', function (data) {
    // this message arrives immediately
    socket.emit('analysis:status', 'Request acknowledged');

    // this process takes about 20 seconds, and uses the callback every 2-3 seconds
    uploader.processFile(data, function () {
        return {
            statusUpdate: function (message) {
                // these messages arrive all at once at the end of processing
                socket.emit('analysis:status', message);
            }
        }
    });
});

function processFile(filename, callback) {

    callback().statusUpdate("Unzipping file");
    var steps = [];

    var zip = new admZip(__dirname + '/' + filename);
    var entries = zip.getEntries();
    var entry = zip.getEntry(entries[0]);

    var bufferStream = new stream.PassThrough;
    bufferStream.end(new Buffer(entry.getData()));

    callback().statusUpdate("Processing stream");
    var xmlStream = flow(bufferStream, { strict: true, preserveMarkup: flow.NEVER, simplifyNodes: false, normalize: false });

    xmlStream.on('end', function () {
        storeResults(steps, callback);
    });

    xmlStream.on('error', function (ex) {
        console.log('xml-flow error', ex);
    });

    xmlStream.on('tag:Step', function (element) {
        steps.push(element);
        if (steps.length % 50 == 0) {
            callback().statusUpdate("Caching step " + stepNumber);
        }
    });
}

更新

嗯,我调查了两个给定的答案,但遗憾的是没有得到满意的结果。

使用process.nextTick并没有多大帮助,因为长时间运行的过程似乎并没有“放弃嘀嗒”直到它完成

使用sax-async可以实现我想要的效果,但需要大约三倍的时间。

所以考虑到选择,我在优化解析(预分析,选择性解析等)方面做了很多工作,并设法将其降低到大约12秒,所以可以告诉用户等待而不能表明进展直到完成。

Bounty奖励给了kio,因为它导致了sax-async,它确实起作用,但速度不够快:(

2 个答案:

答案 0 :(得分:1)

问题的原因

看起来这个过程是CPU密集型任务,可能它完全阻止了事件循环,直到完成计算。 这是因为node.js应用程序通常在单个线程上运行。

一般关于CPU密集型任务

请查看此答案以获取更多详细信息https://stackoverflow.com/a/17957474/4138339

当您执行密集任务时:

  1. 尝试将它们拆分为较小的进程并调用process.nextTick。在文档https://nodejs.org/api/process.html#process_process_nexttick_callback_arg
  2. 中查找更多详细信息
  3. 使用儿童过程工作者。在文档https://nodejs.org/api/child_process.html#child_process_asynchronous_process_creation中查找更多详细信息。
  4. OP问题解决方案

    我专注于xml-flow模块,但是之前的一些任务也可能是密集型的。我查看了xml-flow源代码,我认为我的解决方案适用于您的特定情况。

    当调用此事件'tag:Step'时,它不会等待您的回调完成。但我们暂停xml-flow,做其他事情并恢复。我没有你的应用程序,因此我无法编写完全正常的工作示例。你必须自己写一下。

    对于暂停流使用xmlStream.pause(),然后调用您的回调,并在一段时间后恢复流xmlStream.resume()。我认为,为了快速检查,您可以在超时时调用xmlStream.resume(),但对于生产,最好使用process.nextTick

答案 1 :(得分:1)

nodejs中的

.emit不是异步的。它可能看起来像那样,但它不是真正的异步。当它被调用时,它的所有事件处理程序将逐步同步执行。

看起来xml-flow是sax-js的包装器,尽管.emit调用了{<3}},但它似乎正在进行所有同步。

您需要编写自己的包装器(或者fork xml-flow并更改它)。当然,有人已经做过类似的事情:sax-async