AWS Lambda/API Gateway隔次调用出现内部服务器错误的解决方法
Fix: AWS Serverless API Intermittent 500 Errors Due to Unresolved Database Callbacks
我最近碰到一个头疼的问题:用Node.js + Serverless搭建的API部署到AWS开发环境后,首次调用能正常返回数据,但每隔一次调用就会触发内部服务器错误。排查下来,发现问题出在数据库调用的回调没有及时拿到结果,导致返回结果不一致。
问题根源
原来的数据库调用采用传统回调模式,在Serverless的环境下,由于函数的冷启动、连接池复用等特性,有时候回调无法及时获取查询结果,进而导致函数返回异常。
解决方案:用Promise封装数据库操作
我创建了一个返回Promise的Database类,确保查询结果能被回调及时获取,彻底解决了这个间歇性错误。
Database类代码
'use strict'; const mysql = require('mysql'); /** * Database */ class Database { constructor(config) { if (!this.dbConnection) { console.log('connect to DB'); this.dbConnection = mysql.createPool(config); this.dbConnection.on('connection', (connection) => { console.info('Connection Made!'); }); } } query(sql, args) { return new Promise((resolve, reject) => { this.dbConnection.query(sql, args, (err, rows) => { if (err) { reject(err); } resolve(rows); }) }); } close() { return new Promise((resolve, reject) => { this.dbConnection.end((error) => { if (error) { reject(error); } resolve(); }); }); } } module.exports = Database;
业务逻辑代码(使用Promise化的数据库操作)
'use strict'; const Database = require('./lib/Database'); const {successResponse, errorResponse} = require('./lib/response'); const CategoryResource = require('./resource/Category'); module.exports.list = (event, context, callback) => { let sql = 'SELECT * FROM categories AS c WHERE c.company_id = ? AND c.parent_id IS NULL AND c.status = 1 LIMIT ?, ?;'; const company = parseInt(event.queryStringParameters.company); let page = 1; let limit = 20; if (null != event.queryStringParameters) { if ('page' in event.queryStringParameters) { page = parseInt(event.queryStringParameters.page); } if ('limit' in event.queryStringParameters) { limit = parseInt(event.queryStringParameters.limit); } } let start = (page - 1) * limit; if (isNaN(company)) { callback(null, errorResponse(400, 'Company ID Required', 'Parameter company_id is required.', [])); return; } let Category = new Database(); let categoryResource = []; Category .query(sql, [company, start, limit]) .then(response => { Category.close(); response.forEach((category) => { categoryResource.push(CategoryResource(category)); }); callback(null, successResponse(200, { "total": response.length, "perPage": limit, "currentPage": page, "data": categoryResource })); }) .catch((error) => { callback(null, errorResponse(error.code, error.sqlMessage, error.sql, { code: error.errno, field: error.sqlMessage, message: error.sqlMessage })); Category.close(); }); };
为什么这能解决问题?
通过Promise封装数据库的query和close方法,我们确保了数据库操作的异步流程被正确管控,无论Serverless函数是冷启动还是热启动,回调都能在数据库操作完成后拿到结果,避免了因异步回调未及时执行导致的间歇性500错误。
希望这个方案能帮到遇到同样问题的开发者!
内容的提问来源于stack exchange,提问作者Richard Skinner
相关产品推荐
相关产品推荐

