鉴于NodeJS应用程序通过Hapi提供API,我如何捕获所有错误,以便将它们传达给用户?当发生致命错误时,我需要能够向任何向我们的服务器发出请求的客户端发送500错误。
我们认为我们已经设置好所有内容以正确捕捉所有错误,但这一次完全超越了我们。
我做了“git clone”从Github获取应用程序,然后“npm install”然后“npm run”。然后我使用该应用程序几个小时。然后我关上笔记本电脑回家了。几个小时后,我打开笔记本电脑,我想继续工作。但是与外部数据库的连接已经过时了。这当然有道理,但我们的应用程序应该向客户传达问题。相反,当我运行cURL:
curl "http://nefers-mbp.home:3000/profile/Company/31468" -X GET --header 'Accept: application/json' --header 'x-api-key: 2gt7Pt2LU194KKcNnXHRc564JU'
90秒后,它达到默认的cURL超时:
curl: (52) Empty reply from server
这对我们来说是不可接受的。我希望立即发送错误。
在我运行应用程序的终端窗口中,我看到数据库连接已经过时,导致错误:
160920/111554.302, [ops], memory: 49Mb, uptime (seconds): 24285.523, load: 1.6513671875,2.2822265625,3.7041015625
{ profile_type: 'Company', id: '31468' }
{ method: 'select',
options: {},
bindings: [ '2gt7Pt2LU194KKcNUxJU' ],
sql: 'select `user_id`, `action`, `permission` from `api_permissions` where `api_key` = ?' }
160920/11124.308, [ops], memory: 49Mb, uptime (seconds): 24315.529, load: 1.55640625,2.204105625,3.6259765625
Knex:Error Pool2 - Error: Pool.release(): Resource not member of pool
{ Error: read ETIMEDOUT
at exports._errnoException (util.js:1026:11)
at TCP.onread (net.js:564:26)
--------------------
at Protocol._enqueue (/Users/lsam/projects/mattermare/api/node_modules/mysql/lib/protocol/Protocol.js:141:48)
at Connection.query (/Users/lsam/projects/mattermare/api/node_modules/mysql/lib/Connection.js:201:25)
at /Users/lsam/projects/mattermare/api/node_modules/knex/lib/dialects/mysql/index.js:92:18
at tryCatcher (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/util.js:26:23)
at Promise._resolveFromResolver (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/promise.js:483:31)
at new Promise (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/promise.js:71:37)
at Client._query (/Users/lsam/projects/mattermare/api/node_modules/knex/lib/dialects/mysql/index.js:88:12)
at Client.query (/Users/lsam/projects/mattermare/api/node_modules/knex/lib/client.js:127:24)
at Runner.<anonymous> (/Users/lsam/projects/mattermare/api/node_modules/knex/lib/runner.js:116:24)
at Runner.tryCatcher (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/util.js:26:23)
at Runner.query (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/method.js:15:34)
at /Users/lsam/projects/mattermare/api/node_modules/knex/lib/runner.js:44:21
at /Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/using.js:176:30
at bound (domain.js:280:14)
at runBound (domain.js:293:12)
at tryCatcher (/Users/lsam/projects/mattermare/api/node_modules/bluebird/js/main/util.js:26:23)
code: 'ETIMEDOUT',
errno: 'ETIMEDOUT',
syscall: 'read',
fatal: true }
在控制台中,此错误是即时的,但未与等待的cURL请求进行通信。这是我想要解决的问题。
我去寻找有帮助的npm包,我发现了这个:
https://www.npmjs.com/package/hapi-error
但显然,这只会使错误消息变得漂亮。我们的问题更为根本:我们未能发现这一致命错误。
服务器自动修复自己,所以下次我调用cURL时它有一个活动的数据库连接,所以下一个调用工作得很好。
但是我们需要抓住每一个错误,无论原因是什么,并确保客户端获得合理的消息。我们不能允许客户端只是暂停的情况。
如果我在此之前使用Apache或Nginx,通过反向代理,我会得到它,然后它们可以更快地超时,但是为了正确,我们希望NodeJS应用程序在错误发生时自己发送错误消息发生。
我们已经使用“好”来捕获大多数错误:
https://www.npmjs.com/package/good
我们注册为:
{
"plugin": {
"register": "good",
"options": {
"opsInterval": 30000,
"reporters": [
{
"reporter": "good-console",
"events": {
"log": "*",
"ops": "*",
"request": "*"
}
}
]
}
}
}
但无论出于何种原因,这个错误都没有被发现。
有没有办法捕获每个错误,并向等待的客户端发送500错误?
答案 0 :(得分:0)
你能发布路线代码吗?听起来你在回调中或在承诺链的末尾调用reply
。当错误发生时reply
没有被调用。
如果你使用的是承诺,你可能会做这样的事情:
const myHandler = (req, reply) {
knex('users')
.first()
.where('id', req.params.id)
.then((user) => reply(formatUser(user))
}
如果数据库连接失败,则永远不会调用reply,因此服务器将永远等待。您可以修改它以在承诺链的末尾添加一个catch,即.catch((e) => reply().code(500)
,或者您可以返回一个承诺,hapi会将拒绝的承诺变为500.
const myHandler = (req, reply) {
const p = knex('users')
.first()
.where('id', req.params.id)
.then((user) => formatUser(user))
reply(p);
}
我不知道这是否符合您自动捕获错误的请求,但从Hapi的角度来看,没有错误,reply
从未被调用过。
如果您想为路由设置超时,如果在几毫秒内未调用reply
,则返回503,您可以在路由选项中执行此操作:
server.route({
method: 'GET',
path: '/api/users/{userId}'',
config: {
timeout: {
server: 10000
}
},
handler: myHandler
});
或者在服务器上设置连接时在所有路由上设置超时:
server.connection({ port: 80, routes: { timeout: { server: 10000 } } });
设置超时是一种很好的做法,但仍然必须保证您的处理程序能够以某种方式调用回复回调。这样你的路线就会更快失败。