背景
第二天早上來的時候,發現接口返回異常Cannot enqueue Query after fatal error
從日志看上去,接口是正常運行的,搜索一下之后發現是數據庫的問題,連接斷開了
原因
MySQL中有一個名叫wait_timeout
的變量,表示操作超時時間,當連接超過一定時間沒有活動后,會自動關閉該連接,這個值默認為28800(即8小時)
。對于這種普通連接的方式,在正式線上可能會遇到連接丟失的問題(No reconnection after connection lost
錯誤日志),連接丟失后不會自動重新連接,會觸發error事件。
解決方案
我從網上嘗試了好幾種,有些自測不行,有些可以,我都列出來
- 連接配置中增加
useConnectionPooling: true
,這個我嘗試了不行
let dbconfig = {host: "db_host",user: "db_user",password: "db_pass",database: "db_name",useConnectionPooling: true,debug: true,
};
- 捕獲全局異常,判斷code為
PROTOCOL_CONNECTION_LOST
,然后進行重啟mysql,這個我也嘗試了不行
process.on("uncaughtException", function (err) {if (err.code == "PROTOCOL_CONNECTION_LOST") {mysql.restart();}
});
適用于還有另一個報錯的情況,Error: Connection lost The server closed the connection
- 監聽連接報錯,然后重連,這個可以
function handleError(err) {if (err) {// 如果是連接斷開,自動重新連接if (err.code === "PROTOCOL_CONNECTION_LOST") {connect();} else {console.error(err.stack || err);}}
}// 連接數據庫
function connect() {db = mysql.createConnection(config);db.connect(handleError);db.on("error", handleError);
}var db;
connect();
- 使用連接池的方式,每次查詢完成之后釋放連接池,這個也可行(
我最后使用的是這種
)
var mysql = require("mysql");
var pool = mysql.createPool(config);pool.getConnection(function (err, connection) {connection.query("SELECT something FROM sometable", function (err, rows) {connection.end();});
});
實際代碼
// pool.js
const mysql = require("mysql");
const { logger } = require(process.cwd() + "/middleware/log.js");
const configObj = require("./config");const pool = mysql.createPool({connectionLimit: 10, // 設置連接池限制...configObj,
});pool.on("connection", (connection) => {console.log("數據庫連接成功!");
});pool.on("error", (err) => {logger(JSON.stringify({msg: "數據庫錯誤",error: err.message,}));if (err.code === "PROTOCOL_CONNECTION_LOST") {console.log("數據庫連接丟失,嘗試重新連接...");pool.on("connection");} else {throw err; // 未處理的錯誤}
});module.exports = pool;
// sql.js
const { logger } = require(process.cwd() + "/middleware/log.js");
const pool = require("./pool");function RunSQL(query, params) {return new Promise((resolve, reject) => {pool.getConnection((err, connection) => {if (err) {reject(err);return;}connection.query(query, params, (err, results) => {connection.release();if (err) {reject(err);return;}resolve(results);});connection.on("error", (err) => {console.error("數據庫連接錯誤: ", err);if (err.code === "PROTOCOL_CONNECTION_LOST") {console.log("數據庫連接丟失,嘗試重新連接...");logger(JSON.stringify({msg: "數據庫連接失敗",error: err.message,}));pool.on("connection");}});});});
}module.exports = RunSQL;