首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >了解节点中错误处理的尝试/捕获和域

了解节点中错误处理的尝试/捕获和域
EN

Stack Overflow用户
提问于 2014-05-25 00:17:07
回答 1查看 1.5K关注 0票数 2

我一直在研究如何正确地处理Node中的错误,并在StackOverflow和NodeJS的站点上找到了一些很好的答案,比如如何防止node.js崩溃?试-抓不起作用和NodeJS文档本身:http://nodejs.org/api/domain.html

但是,对于何时何地使用try/catch和/或域,我只剩下几个问题。我意识到这与异步代码和同步代码有关,但即使在NodeJS站点提供的有关域的示例代码中,它们也在域的错误处理程序中使用了try/catch。有人能详细解释一下吗,try/catch不会在错误处理程序中捕获异步错误吗?

除此之外,NodeJS的文档建议您仍然应该在异常情况下结束进程,这就是为什么域文档中的代码建议在异常被捕获时使用集群来分叉一个新的子进程/工作程序。提出的主要原因是:

根据抛出在JavaScript中的工作方式的本质,几乎没有任何方法可以安全地“捡起您停下来的地方”,而不泄漏引用,或者创建其他类型的未定义的脆弱状态。

有人能解释一下吗?如何在Javascript中工作的性质是什么?为什么资源会像疯子一样泄漏?是否总是需要重新启动进程或终止/启动工作人员?

例如,我正在实现mysql,并一度忘记启动本地JugglingDB服务器。我遇到了一个ECONNREFUSED错误,导致进程崩溃。意识到这可能发生在生产环境中(DB崩溃或暂时不可用),我希望捕获此错误并优雅地处理它;重新尝试连接,维护有关DB的状态变量,并可能通过响应临时不可用消息来处理请求。Try/Catch只是没有捕捉到错误,虽然我看到我可以使用一个域,使用推荐的策略,但在DB恢复联机之前,我将处于一个没完没了的杀死和启动工作人员的循环中。

无论出于什么原因,JugglingDB只有一个“连接”事件,但是没有任何类型的回调函数来传递错误对象;它试图在您实例化类并抛出没有捕获并以优雅方式发出的错误时连接。这让我想看看其他的ORM,但这仍然不能回答我关于如何处理这种情况的问题。如果使用域捕获潜在的连接错误并在不启动新进程的情况下优雅地处理它,会不会是错误的?

下面是我提交给JugglingDB github:https://github.com/1602/jugglingdb/issues/405的问题/问题,下面是JugglingDB在服务器不存在时产生的错误的堆栈跟踪(只在启用池选项时发生):

代码语言:javascript
复制
Error: connect ECONNREFUSED
    at errnoException (net.js:901:11)
    at Object.afterConnect [as oncomplete] (net.js:892:19)
    --------------------
    at Protocol._enqueue (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/protocol/Protocol.js:110:48)
    at Protocol.handshake (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/protocol/Protocol.js:42:41)
    at PoolConnection.Connection.connect (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Connection.js:101:18)
    at Pool.getConnection (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Pool.js:42:23)
    at Pool.query (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Pool.js:185:8)
    at initDatabase (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:62:20)
    at initializeConnection (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:49:9)
    at Object.initializeSchema [as initialize] (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:33:5)
    at new Schema (/Users/aaronstorck/Sites/site/node_modules/jugglingdb/lib/schema.js:105:13)
    at Application.loadConnections (/Users/aaronstorck/Sites/site/core/application.js:95:40)

Process finished with exit code 8

谢谢你能帮我理解其中的任何部分!:)

EN

回答 1

Stack Overflow用户

发布于 2014-05-27 13:22:36

看看JugglingDB,并试图连接到我笔记本上一个不存在的mysql服务器,我确实得到了ECONNREFUSED,但是它每隔几秒钟就会记录一次(尝试重新连接),不会使我的进程崩溃。我把杂技数据库-mysql降到了0.0.6,然后我就可以重复你说的话了。我查看了jugglingdb-mysql源代码,发现连接上是否出现了错误,这是抛出的。这是不好的,不是一个好的行为。虽然垃圾邮件日志也很糟糕,但是抛出可以处理的错误更糟糕。所以我建议升级到0.0.7或0.0.8(最新版本),这样您就不用担心这个错误了。我只是为JugglingDB或jugglingdb mysql做了一个错误报告,以便像node一样正确地将错误传播回来。节点-mysql是一个很好的例子,说明它是如何完成的。

现在让我们来看看如何处理node.js中的错误。不是所有的错误都是在node.js中抛出的。例如,您可以这样做:

代码语言:javascript
复制
require('fs').readFile('non-existent-file-yes-gimmie-a-error', function (error) { })

回调将被错误地调用,但不会被抛出。如果您是一个模块开发人员,那么在这种情况下,总是传播错误。要么用错误调用回调,要么调用错误处理程序,或者在事件发射器上发出error事件。以前版本的jugglingdb-mysql是如何处理错误的一个非常糟糕的例子。如果您不能用try catch捕获错误,那么就不应该抛出它。只有当它可以被捕获时才抛出,就像node.js核心库函数一样。如果您执行require('fs').readFile(),它将立即抛出错误,这是可捕捉的。但是,在发现错误的情况下,在函数返回后(在处理异步事件时),它将使用错误调用回调。

现在我相信你已经经历了更多的崩溃,形成了无法捕捉的抛出错误。它们很可能来自事件发射器error事件。在node.js中,当发出error事件时,如果没有处理程序,它将引发。因此,如果您想从事件发射器捕获错误,只需添加一个error事件即可。这方面的一个示例是将返回事件发射器的fs.createReadStream

代码语言:javascript
复制
require('fs').createReadStream('non-exitent-file-gimmie-a-error')

这肯定会导致进程崩溃,但是如果您可以处理这个错误(例如,给404,发送给导致这个错误的http请求),那么添加一个error处理程序,它就不会再抛出错误了:

代码语言:javascript
复制
require('fs').createReadStream('non-exitent-file-gimmie-a-error').on('error', handleError)

除了I/O错误之外,还有类型错误和引用错误。这些都是你必须小心处理它们的。

例如,您可以拥有这样的东西(您不会这样做,但只是为了教育目的):

代码语言:javascript
复制
var routes = {
    '/' : function () {...},
    '/one' : function () {...},
    '/two' : function () {...}
}

require('http').createServer(function (req, res) {
    fs.open('layout.html', 'r', function (err, fd) {
        if (err) return errorHandler(err);
        var buffer = new Buffer(4000); // Lets say we know our file is 4000 bytes exatly

        fs.read(fd, buffer, 0, 4000, function (err, data) {
            if (err) return errorHandler(err);

            try {
                routes[req.url](req, res, data);
                fs.close(fd);
            } catch (e) {
                errorHandler(err);
            }
        });
    });

    function errorHandler(err) {
        res.writeHead(404);
        res.end();
    }
}).listen(1337)

现在,正如您所看到的,如果routes[req.url]不存在,将抛出一个错误,因为我们试图调用errorHandler,但是文件仍然是打开的,并且我们忘记了在错误时关闭它。如果使用错误的URL发出10000个请求,那么您的进程最大打开文件限制就用完了。您可以修复这个问题,但是可以将fs.close(fd)放在finally子句中。

让我们想象一下,但是没有trycatch,而是用全局域捕获错误。在某些情况下,你已经不知道你的程序的状态了,所以在出错的时候你不能决定让应用程序继续运行,因为在这种情况下,它会泄漏文件描述符。

这个问题适用于任何需要编写清理代码的地方,而且作为开发人员,我们并不总是考虑所有不同的输入,而且我们总是会出错。这就是为什么建议打破这一进程。您可以使用process.on('uncaughtException')domain.on('error')捕获错误,但是您必须在完成清理之后终止该进程。

但你得小心点。当您不知道错误来自何处时,请尝试崩溃。如果您确实知道它是从哪里来的(如上面所述,我们只打开了一个文件),那么清理它的资源并让您的进程继续进行,因为攻击者可以找到如何使用恶意输入和DOS破坏您的进程。

在您决定做一些事情然后崩溃的情况下,请确保设置了超时,然后在超时发生时使进程崩溃。unref节点v0.10上的超时,以确保它不会使进程处于活动状态,而在v0.8上,您可以使用像addTimeout这样的模块来清除回调调用时的超时。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23850866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档