ZombieJS:从 for 循环重复调用时间歇性崩溃

Posted

技术标签:

【中文标题】ZombieJS:从 for 循环重复调用时间歇性崩溃【英文标题】:ZombieJS: intermittently crashes when called repeatedly from a for loop 【发布时间】:2016-06-04 10:37:42 【问题描述】:

我在 Heroku 上有一个 ZombieJS 节点服务器,用于从互联网上抓取数据。服务器代码从客户端的for 循环中调用。循环的每次迭代都会进行一次服务器调用,从而使僵尸刮擦。有时,服务器会因以下错误而崩溃。只有当for 循环不止一次迭代时才会发生这种情况。

如何使代码足够健壮,以处理多个同时进行的客户端调用,每个调用都有一个for 循环。

代码:

var express = require('express');
var app = express();
var Browser = require('zombie');    // tried changing var to const; no difference
var assert = require('assert');

app.set('port', (process.env.PORT || 5000));

var printMessage = function()  console.log("Node app running on " + app.get('port')); ;

var getAbc = function(response, input)

    var browser = new Browser(); 
    browser.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0'; 
    browser.runScripts = true;
    var url = "http://www.google.com/ncr"; 

    browser.visit(url, function() 
        browser.fill('q', input).pressButton('Google Search', function()
            // parsing number of results from browser object

            response.writeHead(200, 'Content-Type': 'text/plain');
            response.end(numberOfSearchResults); 
        );
    );


var handleXyz = function(request, response)

    getAbc(response, request.query.input); 


app.listen(app.get('port'), printMessage); 
app.post('/xyz', handleXyz); 

错误:

 assert.js:86
   throw new assert.AssertionError(
              ^
 No open window with an html document
     at Browser.field (/app/node_modules/zombie/lib/index.js:811:7)
     at Browser.fill (/app/node_modules/zombie/lib/index.js:903:24)
     at /app/cfv1.js:42:11
     at done (/app/node_modules/zombie/lib/eventloop.js:589:9)
     at timeout (/app/node_modules/zombie/lib/eventloop.js:594:33)
     at Timer.listOnTimeout (timers.js:119:15)

我有一个使用 HorsemanJS/PhantomJS 的类似项目,它以类似的方式失败(我也坚持这一点!):NodeJS server can't handle multiple users

【问题讨论】:

我进行了快速测试,它似乎工作正常。能否添加 browser.debug() 并查看是否可以从日志中获取更多信息? 您是否从for 循环中反复调用它?它只是有时会失败。 我会把browser.debug()放在哪里? 我会在你实例化浏览器之后放它(即 - var browser = new Browser(); browser.debug(); 我能够重现。我实际上是在测试加载我自己的网站而不是谷歌(出于各种原因),但实际上使用谷歌会导致错误。我猜谷歌正在返回一个不同的响应 b/c 你在短时间内发送了太多的请求。我可以在今天晚些时候仔细查看确切的问题。 【参考方案1】:

我看到您正在为每次调用创建一个 Browser 对象的新实例。我的猜测是之前的“浏览器”仍在关闭,或者当下一个调用试图打开另一个时,垃圾收集器尚未处理。尝试将 Browser 的实例化移到 getAbc() 之外

【讨论】:

我尝试将 var browser = new Browser() 移动到 require 块下方,但没有帮助。【参考方案2】:

总的来说,我认为您应该小心,或者避免向远程服务器生成大量未经请求的请求。许多网站会限制您和/或开始拒绝连接。话虽如此,我相信我在这个特殊案例中找到了问题的根源。

我测试了代码 sn-p,对于这种特殊情况,如果您发出过多请求,Google 将重置连接。当连接被重置时,其中一个变量最终会导致断言失败。

重置连接时出现的错误:

  zombie TypeError: read ECONNRESET
    at zombie/lib/pipeline.js:89:15
    at tryCatcher (zombie/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (zombie/node_modules/bluebird/js/release/promise.js:497:31)
    at Promise._settlePromise (zombie/node_modules/bluebird/js/release/promise.js:555:18)
    at Promise._settlePromise0 (zombie/node_modules/bluebird/js/release/promise.js:600:10)
    at Promise._settlePromises (zombie/node_modules/bluebird/js/release/promise.js:679:18)
    at Async._drainQueue (zombie/node_modules/bluebird/js/release/async.js:125:16)
    at Async._drainQueues (zombie/node_modules/bluebird/js/release/async.js:135:10)
    at Immediate.Async.drainQueues [as _onImmediate] (zombie/node_modules/bluebird/js/release/async.js:16:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

我将您的原始错误进一步降低,但问题的根源实际上是由于上述原因。当发生上述情况时,它会导致 document.documentElement 为 false-y 值,并随后导致 field 函数中的zombie/lib/index.js 中的此断言失败:

assert(this.document && this.document.documentElement, 'No open window with an HTML document');

我认为最简单的解决方案是在客户端处理错误并尝试优雅地恢复。

【讨论】:

这很棒;非常感谢您。关于如何优雅地处理错误有什么建议吗? 这取决于您的应用程序所需的用户体验。我假设您可以检测到响应何时是错误的。您可以显示存在错误并要求用户重试或自动重试。如果您自动重试,您可能需要指数回退之类的东西,因为服务器本质上是超载的。理想情况下,您会在服务器上捕获错误以使其不会崩溃,但我认为您需要修改 Zombie 以引发异常而不是 AssertionError(我不知道是否有办法从失败中恢复断言)。我想这将是更多的工作。

以上是关于ZombieJS:从 for 循环重复调用时间歇性崩溃的主要内容,如果未能解决你的问题,请参考以下文章

编译器优化可以消除在 for 循环的条件中重复调用的函数吗?

对表格进行排序时,Vue v-for循环中的重复键

从 For 循环中删除重复项

Android “只有创建视图层次结构的原始线程才能接触其视图。” for循环中的错误[重复]

shell里面的for循环

For或While循环跳过输入[重复]