Воспроизведение ошибки MySQL: сервер закрыл соединение (node.js)
Я пытаюсь воспроизвести ошибку MySQL, которую я вижу в моем приложении node.js на EC2 с node библиотекой mysql:
Соединение потеряно: сервер закрыл соединение.
Я не могу воспроизвести ошибку, локально убивая базу данных, обрабатывая мой код очень просто - он просто перечитывает каждые несколько секунд и снова подключается к db после его перезапуска. На EC2 это происходит примерно в 4 часах Тихого океана, но db все еще работает и работает нормально.
Я хотел бы
- Воспроизводите сбой с моей локальной mysql
- Добавьте любую логику, которая мне нужна в моем вспомогательном модуле mysql для обработки этого
Здесь ошибка в моем node.js приложении:
2012-10-22T08: 45: 40.518Z - ошибка: uncaughtException date = Mon Oct 22 2012 08:45:40 GMT + 0000 (UTC), pid = 14184, uid = 0, gid = 0, cwd =/home/ec2-user/my-app, execPath =/usr/bin/nodejs, version = v0.6.18, argv = [/usr/local/bin/ node, /home/ec 2-user/my-app/app.js, --my-app], rss = 15310848, heapTotal = 6311392, heapUsed = 5123292, loadavg = [0.0029296875, 0.0146484375, 0.04541015625], время безотказной работы = 3238343.511107486, trace = [column = 13, Файл =/дом/ec2 пользователь/мой-приложение/node_modules/mysql/lib/protocol/Protocol.js, function = Protocol.end, line = 63, method = end, native = false, column = 10, file = stream.js, function = Socket.onend, строка = 80, метод = onend, native = false, column = 20, file = events.js, function = Socket.emit, line = 88, method = emit, native = false, column = 51, file = net.js, function = TCP.onread, line = 388, method = onread, native = false], stack = [Ошибка: соединение потеряно: сервер закрыл соединение.,
на Protocol.end(/home/ec2-user/my-app/node_modules/mysql/lib/protocol/Protocol.js:63:13), на Socket.onend(stream.js: 80: 10), на Socket.emit(events.js: 88: 20), в файле TCP.onread(net.js: 388: 51)]
Здесь мой код (вспомогательный модуль mysql):
module.exports = function (conf,logger) {
var mysql = require('mysql');
var connectionState = false;
var connection = mysql.createConnection({
host: conf.db.hostname,
user: conf.db.user,
password: conf.db.pass,
database: conf.db.schema,
insecureAuth: true
});
function attemptConnection(connection) {
if(!connectionState){
connection = mysql.createConnection(connection.config);
connection.connect(function (err) {
// connected! (unless `err` is set)
if (err) {
logger.error('mysql db unable to connect: ' + err);
connectionState = false;
} else {
logger.info('mysql connect!');
connectionState = true;
}
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
connectionState = false;
/*
if (!err.fatal) {
return;
}
if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
throw err;
}
*/
});
}
}
attemptConnection(connection);
var dbConnChecker = setInterval(function(){
if(!connectionState){
logger.info('not connected, attempting reconnect');
attemptConnection(connection);
}
}, conf.db.checkInterval);
return connection;
};
Ответы
Ответ 1
Вот что я в итоге использовал, и он работал очень хорошо. При случайном отключении/перезагрузке соединение восстанавливается. У меня есть файл database.js, который устанавливает соединения и периодически их проверяет.
Чтобы сделать запрос:
var conn = require('./database');
var sql = 'SELECT foo FROM bar;';
conn.query(sql, [userId, plugId], function (err, rows) {
// logic
}
Здесь моя databbase.js
var mysql = require('mysql');
var Common = require('./common');
var conf = Common.conf;
var logger = Common.logger;
var connectionState = false;
var connection = mysql.createConnection({
host: conf.db.hostname,
user: conf.db.user,
password: conf.db.pass,
database: conf.db.schema,
insecureAuth: true
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
connectionState = false;
});
function attemptConnection(connection) {
if(!connectionState){
connection = mysql.createConnection(connection.config);
connection.connect(function (err) {
// connected! (unless `err` is set)
if (err) {
logger.error('mysql db unable to connect: ' + err);
connectionState = false;
} else {
logger.info('mysql connect!');
connectionState = true;
}
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
if (!err.fatal) {
//throw err;
}
if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
//throw err;
} else {
connectionState = false;
}
});
}
}
attemptConnection(connection);
var dbConnChecker = setInterval(function(){
if(!connectionState){
logger.info('not connected, attempting reconnect');
attemptConnection(connection);
}
}, conf.db.checkInterval);
// Mysql query wrapper. Gives us timeout and db conn refreshal!
var queryTimeout = conf.db.queryTimeout;
var query = function(sql,params,callback){
if(connectionState) {
// 1. Set timeout
var timedOut = false;
var timeout = setTimeout(function () {
timedOut = true;
callback('MySQL timeout', null);
}, queryTimeout);
// 2. Make query
connection.query(sql, params, function (err, rows) {
clearTimeout(timeout);
if(!timedOut) callback(err,rows);
});
} else {
// 3. Fail if no mysql conn (obviously)
callback('MySQL not connected', null);
}
}
// And we present the same interface as the node-mysql library!
// NOTE: The escape may be a trickier for other libraries to emulate because it looks synchronous
exports.query = query;
exports.escape = connection.escape;
Ответ 2
Проверьте функцию пула mysql в node-mysql
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'bob',
password : 'secret'
});
pool.getConnection(function(err, connection) {
// connected! (unless `err` is set)
connection.end();
});
Ответ 3
У меня были подобные проблемы и я создал функцию-оболочку getConnection(), которая проверяет работоспособность соединения mysql, прежде чем возвращать его вызывающему, и при необходимости восстанавливает соединение. В моем тестировании он прозрачно обрабатывал фатальные и нефатальные проблемы подключения для приложения. Если соединение просто было отключено, приложение восстанавливается без каких-либо ошибок. Если есть проблема с временным, но фатальным подключением к базе данных, приложение возобновит работу автоматически, как только база данных будет доступна снова.
Чтобы воспроизвести проблему для тестирования, добавьте следующие две строки в файл my.ini или my.cnf в блоке [mysqld]
:
interactive_timeout=30
wait_timeout=30
Вот содержимое файла, который я назвал "database.js":
var mysql = require("mysql");
var CONFIG = require(__dirname + "/configuration");
module.exports.getConnection = function() {
// Test connection health before returning it to caller.
if ((module.exports.connection) && (module.exports.connection._socket)
&& (module.exports.connection._socket.readable)
&& (module.exports.connection._socket.writable)) {
return module.exports.connection;
}
console.log(((module.exports.connection) ?
"UNHEALTHY SQL CONNECTION; RE" : "") + "CONNECTING TO SQL.");
var connection = mysql.createConnection({
host : CONFIG.db.host,
user : CONFIG.db.user,
password : CONFIG.db.password,
database : CONFIG.db.database,
port : CONFIG.db.port
});
connection.connect(function(err) {
if (err) {
console.log("SQL CONNECT ERROR: " + err);
} else {
console.log("SQL CONNECT SUCCESSFUL.");
}
});
connection.on("close", function (err) {
console.log("SQL CONNECTION CLOSED.");
});
connection.on("error", function (err) {
console.log("SQL CONNECTION ERROR: " + err);
});
module.exports.connection = connection;
return module.exports.connection;
}
// Open a connection automatically at app startup.
module.exports.getConnection();
// If you've saved this file as database.js, then get and use the
// connection as in the following example:
// var database = require(__dirname + "/database");
// var connection = database.getConnection();
// connection.query(query, function(err, results) { ....
Ответ 4
Используя общий пул, я написал что-то, что работает локально. Наверное, я проверю его и посмотрю, не сработает ли он причудливо на стороне сервера.
// Test node connection pool stuff
// Create a MySQL connection pool with
// a max of 10 connections, a min of 2, and a 30 second max idle time
var poolModule = require('generic-pool');
var pool = poolModule.Pool({
name : 'mysql',
create : function(callback) {
var Client = require('mysql').Client; // use node-mysql library in all it dubious glory
var c = new Client();
c.user = 'root';
c.password = 'xxx';
c.database = 'test';
c.on('close', function (err) {
console.log('mysqldb conn close');
});
c.on('error', function (err) {
console.log('mysqldb error: ' + err);
});
// parameter order: err, resource
// new in 1.0.6
callback(null, c);
},
destroy : function(client) { client.end(); },
max : 10,
// optional. if you set this, make sure to drain() (see step 3)
min : 2,
// specifies how long a resource can stay idle in pool before being removed
idleTimeoutMillis : 30000,
// if true, logs via console.log - can also be a function
log : true
});
var http = require('http');
http.createServer(function (req, res) {
// Get db conn
pool.acquire(function(err, client) {
if (err) {
// handle error - this is generally the err from your
// factory.create function
console.log('pool.acquire err: ' + err);
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
err: err
}
res.end(JSON.stringify(out));
}
else {
client.query("select * from foo", [], function(err, results) {
if(err){
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
err: err
}
res.end(JSON.stringify(out));
} else {
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
results: results
}
res.end(JSON.stringify(out));
}
// return object back to pool
pool.release(client);
});
}
});
}).listen(9615);
Довольно, пожалуйста, не умирайте в 4 утра без видимых причин!