gpt4 book ai didi

javascript - 了解 Node 的错误处理的尝试/捕获和域

转载 作者:行者123 更新时间:2023-11-29 02:25:06 25 4
gpt4 key购买 nike

我一直在研究处理Node中错误的正确方法,并在StackOverflow和NodeJS的站点上找到了一些不错的答案,例如How do I prevent node.js from crashing? try-catch doesn't work和NodeJS文档本身:http://nodejs.org/api/domain.html

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

除此之外,NodeJS的文档建议您仍应在异常上终止流程,这就是Domain文档的代码建议在捕获异常时使用群集派生新的子流程/ worker 的原因。给出的主要原因是:

By the very nature of how throw works in JavaScript, there is almost never any way to safely "pick up where you left off", without leaking references, or creating some other sort of undefined brittle state.



有人可以解释吗? Javascript中throw如何工作的本质是什么?为什么资源会疯狂地泄漏?而且是否真的真的有必要重新启动该进程或杀死/启动一个工作人员?

例如,我正在实现 JugglingDB ORM,但有一次忘记启动本地mysql服务器。我遇到了 ECONNREFUSED错误,导致程序崩溃。意识到这可能会在生产环境中发生(数据库崩溃或暂时不可用),我想捕获此错误并优雅地处理它;重试连接,维护有关数据库的状态变量,并可能通过以暂时不可用的消息进行响应来处理请求。 Try/Catch根本无法捕获错误,尽管我看到我可以使用推荐的策略来使用域,但是在数据库重新联机之前,我将陷入无休止的死活并启动工作程序的循环。

不管出于何种原因, JugglingDB都只有一个“connected”事件,但没有任何传递错误对象的回调函数。它会在实例化类时立即进行连接,并引发错误,这些错误不会以优美的方式捕获和发出。这使我想看看其他ORM,但这仍然无法回答我关于如何处理这种情况的问题。使用域来捕获潜在的连接错误并在不启动新进程的情况下优雅地处理它是否会出错?

这是我发布到JugglingDB github上的问题/问题: https://github.com/1602/jugglingdb/issues/405,这是来自当服务器不存在时JugglingDB产生的错误的堆栈跟踪( 仅在启用pooling选项时才发生):
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

在此先感谢您可以帮助我理解的任何部分! :)

最佳答案

查看JugglingDB,并尝试连接到笔记本电脑上不存在的mysql服务器,我确实收到ECONNREFUSED,但是它仅每隔几秒钟记录一次(请尝试重新连接),并且不会使我的进程崩溃。我将我的jugglingdb-mysql降级为0.0.6,然后可以复制您所说的内容。我查看了jugglingdb-mysql的源代码,发现是否发现了连接错误,只是抛出了错误。那是不好的,不是很好的行为。尽管垃圾邮件日志也很糟糕,但是引发可处理的错误的情况更糟。因此,我建议升级到0.0.7或0.0.8(最新),这样您就不必担心此错误。我只是为JugglingDB或jugglingdb-mysql作一个错误报告,以像node-mysql一样正确地传播错误。 node-mysql是一个很好的例子。

现在让我们来看一下如何处理node.js中的错误。并非所有错误都引发在node.js中。例如,您可以执行以下操作:

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事件时,为 if there are no handlers it will thrown。因此,如果您想捕获事件发射器的错误,只需添加一个 error事件。一个示例是 fs.createReadStream,它将返回事件发射器:
require('fs').createReadStream('non-exitent-file-gimmie-a-error')

这肯定会使该过程崩溃,但是如果您可以处理该错误(例如,将404发送给导致此错误的http请求),则添加一个 error处理程序,它将不再抛出错误:
require('fs').createReadStream('non-exitent-file-gimmie-a-error').on('error', handleError)

除了I/O错误之外,还有类型和引用错误。这些是您必须谨慎处理的内容。

例如,您可以拥有类似这样的东西(您不会这么做,而只是出于教育目的):
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,所以将引发错误,但是该文件保持打开状态,并且我们忘记在出错时将其关闭。如果发出10000个带有错误URL的请求,则表明您已用完进程的最大打开文件数限制。您可以解决此问题,但可以将 fs.close(fd)放在finally子句中。

让我们想象一下,但是没有 trycatch,但是使用全局域捕获了错误。在某些情况下,您将不再知道程序的状态,因此在出现错误时,您不能仅决定让应用程序继续运行,因为在这种情况下,它将泄漏文件描述符。

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

但是您必须对此小心。当您不知道错误来自哪里时,请尝试崩溃。如果您确实知道它来自哪里(像在上面的例子中一样,我们只打开了一个文件),请清理它的资源,然后继续执行您的进程,因为攻击者可以找到如何使用恶意输入使您的进程崩溃并DOS的。

如果您决定做一些事情然后崩溃,请确保设置了超时,然后在发生超时时使进程崩溃。 unref在节点v0.10上设置超时,以确保它不会使进程保持 Activity 状态;在v0.8上,您可以使用 addTimeout这样的模块,该模块将在调用回调时清除超时。

关于javascript - 了解 Node 的错误处理的尝试/捕获和域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23850866/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com