PhantomJS:确保在server.listen(...)中响应对象保持活动状态

6

我正在使用PhantomJS中的server.listen(...)。我意识到这在很大程度上是试验性的,并且不应该在生产中使用。我正在使用它来创建一个简单的截图服务器,用于为URL生成截图;这是一个玩具项目,我正在使用PhantomJS进行实验。特别是对于长时间运行的请求,我注意到了一个问题,其中response对象不可用。以下是我的代码中相关的片段:

var service = server.listen(8080, function (request, response) {

    response.statusCode = 200;

    if (loglevel === level.VERBOSE) {
        log(request);
    } else {
        console.log("Incoming request with querystring:", request.url);
    }

    var params = parseQueryString(request.url);
    if (params[screenshotOptions.ACTION] === action.SCREENSHOT) {
        getScreenshot(params, function (screenshot) {

            response.headers["success"] = screenshot.success; //<-- here is where I get the error that response.headers is unavailable. Execution pretty much stops at that point for that particular request.
            response.headers["message"] = screenshot.message;

            if (screenshot.success) {
                response.write(screenshot.base64);
            } else {
                response.write("<html><body>There were errors!<br /><br />");
                response.write(screenshot.message.replace(/\n/g, "<br />"));
                response.write("</body></html>");
            }

            response.close();
        });
    } else {
        response.write("<html><body><h1>Welcome to the screenshot server!</h1></body></html>")   
        response.close();
    }
});

getScreenshot是一个异步方法,它使用WebPage.open(...)函数打开一个网页;这个函数也是异步的。所以看起来发生的情况是,当传递给getScreenshot作为参数的回调最终被调用时,response对象已经被删除了。我基本上从PhantomJS得到以下错误:

Error: cannot access member `headers' of deleted QObject

我认为这是因为请求超时而导致连接关闭。文档提到需要至少调用一次 response.write("") 来确保连接保持打开状态。我尝试在 server.listen(...) 的开头调用 response.write(""),甚至使用了一个相当巧妙的解决方案,即使用 setInterval(...) 每隔 500 毫秒执行一次 response.write("")(我甚至将其降低到了 50 毫秒),并确保在结束后清除间隔。然而,我仍然遇到了这个问题。
这是我必须处理的问题,直到他们使 webserver 模块更加健壮吗?还是有办法绕过它?
2个回答

8
我成功地解决了这个问题。似乎在使用WebPage.open加载某些页面(例如http://fark.com和http://cnn.com)时,会触发多个onLoadFinished事件。这导致WebPage.open中的回调函数被多次调用。因此,在控制返回到调用函数时,我已经关闭了响应,因此response对象不再有效。我通过在调用WebPage.open函数之前创建一个标志来解决这个问题。在回调函数中,我检查标志的状态,以查看是否已经遇到了先前的onLoadFinished事件。一旦我完成了WebPage.open回调函数内部要做的事情,我就更新标志以显示我已经完成处理。这样,虚假的(至少在我的代码上下文中是虚假的)onLoadFinished事件就不再被处理。

1
我还将服务器的keepalive设置为false,这似乎也有所帮助——service = server.listen(port, { keepAlive: false }, function (request, response) {.... - chrismarx
1
你太棒了。感谢你分解了如何解决你的问题。我也遇到了同样的问题。 - Eric G
关于Chris上面的评论 - 至少在PhantomJS 1.9.7中,keepAlive默认为false - Whymarrh

0

(请注意,以下内容是针对PhantomJS 1.9.7,而提问者可能是指1.6.1或更早版本。)

如果多个onLoadFinished事件正在触发,您可以使用{{link1:page.open()}}而不是自己监听onLoadFinished。使用page.open()将在私有处理程序中包装您的处理程序,以确保仅调用一次回调。

来自{{link2:源代码}}:

definePageSignalHandler(page, handlers, "_onPageOpenFinished", "loadFinished");
page.open = function (url, arg1, arg2, arg3, arg4) {
    var thisPage = this;
    if (arguments.length === 1) {
        this.openUrl(url, 'get', this.settings);
        return;
    }
    else if (arguments.length === 2 && typeof arg1 === 'function') {
        this._onPageOpenFinished = function() {
            thisPage._onPageOpenFinished = null;
            arg1.apply(thisPage, arguments);
        }
        this.openUrl(url, 'get', this.settings);
        return;
    }
// ... Truncated for brevity

这个功能与其他答案完全相同,作为官方API的一部分公开。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接