NodeJS中IPInfo.io请求异常逃逸try-catch致服务器崩溃排查
NodeJS服务器中IPInfo.io调用错误逃逸try-catch导致崩溃的问题
我搭建了一个NodeJS服务器,用IPInfo.io查询访客所属国家,但偶尔会因为IPInfo.io的响应错误导致服务器崩溃,错误信息如下:
SyntaxError: Unexpected token o in JSON at position 1 at JSON.parse (<anonymous>) at IncomingMessage.<anonymous> (~/server/node_modules/node-ipinfo/dist/src/ipinfoWrapper.js:93:43)
这是我唯一使用IPInfo的代码:
const IPinfo = require('node-ipinfo'); async function getVisitor(req) { // .... console.log(req.originalUrl, " :: ", req.ip, " :: ", ua); let ip = req.ip; if (ip.startsWith("::ffff:")) { ip = ip.replace(new RegExp("^::ffff:"), ''); } console.log("Requesting IP information for " + ip); let countryCode; let ipinfo = new IPinfo(config.ipinfo); try { let info = await ipinfo.lookupIp(ip); countryCode = info.countryCode; } catch (e) { console.error("\n# IP Info error for ip address: " + ip + " (" + req.ip + ")") countryCode = undefined; } }
我搞不懂为什么这个错误能逃出try-catch块,大部分时候代码运行正常,但偶尔触发错误就会导致服务器重启,相关错误日志和输出日志如下:
错误日志:
[2024-06-18T03:08:59.465Z] SyntaxError: Unexpected token o in JSON at position 1 at JSON.parse (<anonymous>) at IncomingMessage.<anonymous> (~/server/node_modules/node-ipinfo/dist/src/ipinfoWrapper.js:93:43) at IncomingMessage.emit (node:events:513:28) at IncomingMessage.emit (node:domain:489:12) at emitCloseNT (node:internal/streams/destroy:138:10) at processTicksAndRejections (node:internal/process/task_queues:82:21) [2024-06-18T03:09:03.510Z] # HTTPS server started
输出日志:
[2024-06-18T03:08:59.222Z] / :: 147.185.132.177 :: Expanse, a Palo Alto Networks company, searches across the global IPv4 space multiple times per day to identify customers' presences on the Internet. If you would like to be excluded from our scans, please send IP addresses/domains to: scaninfo@paloaltonetworks.com [2024-06-18T03:08:59.228Z] Requesting IP information for 147.185.132.177 [2024-06-18T03:09:03.341Z] # server starting...
我查过相关资料,但觉得我的情况不一样,因为我用了await来请求IPInfo。想问为什么错误会逃逸try-catch块,或者该怎么追踪错误来源?
问题原因分析
- 这个错误并非来自
lookupIp的Promise拒绝,而是来自IncomingMessage的close事件回调里的JSON解析错误。node-ipinfo库内部处理响应流时,没有正确捕获解析错误,导致错误作为未捕获的事件抛出,跳出了你的try-catch范围。 - 当响应流关闭时触发的回调执行
JSON.parse,如果返回的内容不是合法JSON(比如已被解析过一次,或者返回了非JSON格式的错误内容),就会抛出SyntaxError,而这个回调没有被包裹在Promise的错误处理链里,所以你的try-catch抓不到。
解决方案
1. 全局捕获未处理的异常
在服务器启动代码里添加全局错误捕获,避免服务器直接崩溃:
// 捕获未处理的Promise拒绝 process.on('unhandledRejection', (reason, promise) => { console.error('未处理的Promise拒绝:', reason, promise); }); // 捕获未捕获的异常 process.on('uncaughtException', (err) => { console.error('未捕获的异常:', err); // 可选:这里可以添加优雅重启服务器的逻辑 });
2. 排查或更换IPInfo库
- 检查
node-ipinfo的最新版本是否修复了该bug,若未修复,可在其仓库提交issue说明错误场景和日志。 - 若库的问题无法及时解决,可放弃使用该库,手动用
fetch或axios调用IPInfo API,自己控制响应解析和错误捕获:
async function getVisitor(req) { // ... 其他代码 let countryCode; try { const response = await fetch(`https://ipinfo.io/${ip}/json?token=${config.ipinfo}`); if (!response.ok) { throw new Error(`HTTP请求失败,状态码: ${response.status}`); } const info = await response.json(); countryCode = info.country; } catch (e) { console.error("\n# IP Info查询错误,IP地址: " + ip + " (" + req.ip + ")", e); countryCode = undefined; } }
3. 临时规避:增加响应格式校验
如果坚持使用原库,可以尝试在调用前增加IP合法性校验,避免向IPInfo发送无效IP导致异常响应:
// 简单的IP格式校验函数 function isValidIp(ip) { return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip); } async function getVisitor(req) { // ... 其他代码 if (!isValidIp(ip)) { console.warn("无效IP地址,跳过IPInfo查询:", ip); countryCode = undefined; return; } // 后续IPInfo调用逻辑 }
内容的提问来源于stack exchange,提问作者ginjaemocoes
相关产品推荐
相关产品推荐

