Node中MySQL连接池的更换以及MySQL连接池集群的使用
做业务逻辑,无论你在用户前面摆多少层花哨的东西,说到底是用户在跟数据库交流。
好像每做一个项目,数据库连接池都不可能不用,而不同的每种语言和不同的每种数据库连接池的调研与选择又不得不花去几天时间,真正写出来,测试是不是真正符合你的需求又是几天时间。
在Node中,我最初选择的是generic pool,原因是上次假期调用到,有看了下主页,感觉更新及时,还挺不错,所以就用了,使用的代码如下:
/**
* Created by randy on 2017/6/29.
*/
var genericPool = require('generic-pool');
var DbDriver = require('mysql');
var settings = require('./settings');
/**
* Step 1 - Create pool using a factory object
*/
const factory = {
create: function () {
return new Promise(function (resolve, reject) {
var client = DbDriver.createConnection({
host: settings.mysql.host,
port: settings.mysql.port,
database: settings.mysql.database,
user: settings.mysql.user,
password: settings.mysql.password,
charset: settings.mysql.charset
});
client.connect(function (err) {
if (err) {
console.log("SQL CONNECT ERROR: ", err);
} else {
console.log("SQL CONNECT SUCCESSFUL.");
resolve(client)
}
});
})
},
destroy: function (client) {
return new Promise(function (resolve) {
client.on('end', function () {
resolve()
});
client.disconnect()
})
},
error: function () {
console.info('AAAAAAAA');
this.create();
}
};
var opts = {
max: 10, // maximum size of the pool
min: 5, // minimum size of the pool
// acquireTimeoutMillis: 2,
idleTimeoutMillis: 1000,
log: true
};
var myPool = genericPool.createPool(factory, opts);
/**
* Step 2 - Use pool in your code to acquire/release resources
*/
// acquire connection - Promise is resolved
// once a resource becomes available
const resourcePromise = myPool.acquire()
.then(function (resource) {
console.log('resource');
})
.catch(function (err) {
console.log(err);
return myPool.drain()
});
module.exports = {
doGetQuery: function (res, procName, handler) {
var paraArray = [];
var stmt = "CALL " + procName + "()";
resourcePromise.then(function (client) {
client.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
//myPool.release(client);
});
})
.catch(function (err) {
// handle error - this is generally a timeout or maxWaitingClients
// error
});
},
doPostQuery: function (req, res, procName, handler) {
req.on("data", function (chunk) {
var params = JSON.parse(chunk.toString());
var paraArray = [];
var stmt = "CALL " + procName + "(";
for (var param in params) {
stmt = stmt + "?, ";
paraArray.push(params[param]);
}
stmt = stmt.substring(0, stmt.length - 2) + ")";
resourcePromise.then(function (client) {
client.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
//myPool.release(client);
});
})
.catch(function (err) {
// handle error - this is generally a timeout or maxWaitingClients
// error
});
}
)
}
};
/**
* Step 3 - Drain pool during shutdown (optional)
*/
// Only call this once in your application -- at the point you want
// to shutdown and stop using this pool.
/*
myPool.drain().then(function() {
myPool.clear();
});
*/
无奈事实证明,这个连接池会在MySQL服务端主动关闭连接后,不仅不自动重连,还会把Node进程弄挂,不知是我设置有问题还是别的,正好有个契机调研到MySQL模块自带连接池也不错,完全逆反常理,真乃良心模块。
var mysql = require('mysql');
var settings = require("./settings");
var config = {
host: settings.mysql.host,
port: settings.mysql.port,
database: settings.mysql.database,
user: settings.mysql.user,
password: settings.mysql.password,
charset: settings.mysql.charset,
acquireTimeout: settings.mysql.acquireTimeout,
waitForConnections: settings.mysql.waitForConnections,
connectionLimit: settings.mysql.connectionLimit,
queueLimit: settings.mysql.queueLimit
};
var pool = mysql.createPool(config);
pool.getConnection(function (err, connection) {
console.log(err);
});
module.exports = {
doGetQuery: (res, procName, handler) => {
let paraArray = [];
let stmt = "CALL " + procName + "()";
pool.getConnection(function (err, connection) {
connection.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
});
connection.release();
});
},
doPostQuery: function (req, res, procName, handler) {
req.on("data", function (chunk) {
let params = JSON.parse(chunk.toString());
let paraArray = [];
let stmt = "CALL " + procName + "(";
for (let param in params) {
stmt = stmt + "?, ";
paraArray.push(params[param]);
}
stmt = stmt.substring(0, stmt.length - 2) + ")";
pool.getConnection((err, connection) => {
connection.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
});
connection.release();
});
}
)
}
};
至此,连接池完毕,相比Generic pool
,MySQL Pool
有个缺点是没有常用连接池的一些配置,比如最小连接数,网上有人总结出数据库连接池常用配置:
{
connectionLimit : 50,
queueWaitTimeout : 10000, // Same as acquireTimeout.
pingCheckInterval : 10000, // The connection used in 10 seconds is reused without ping check.
startConnections : 10, // 10 connections are created when the pool is started.
minSpareConnections : 10, // 10 spare connections should be kept in the pool at all times.
maxSpareConnections : 20, // No more than 20 spare connections.
spareCheckInterval : 300000 // Check the spare connections every 5 minutes.
}
不过问题不大,后来根据需求,又为MySQL Pool
加了集群,这是在看Node MySQL
时无意发现的,这可是个好东西,加上加上:
var mysql = require('mysql');
var settings = require("./settings");
var poolcfg = require("./msqNodeCfg");
var poolCluster = mysql.createPoolCluster();
poolCluster.add(settings.mysqlPoolCluster);
poolCluster.add('X', poolcfg.node01);
poolCluster.add('Y', poolcfg.node02);
poolCluster.add('Z', poolcfg.node03);
module.exports = {
doGetQuery: (res, procName, handler) => {
let paraArray = [];
let stmt = "CALL " + procName + "()";
poolCluster.getConnection(function (err, connection) {
connection.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
});
connection.release();
});
},
doPostQuery: function (req, res, procName, handler) {
req.on("data", function (chunk) {
let params = JSON.parse(chunk.toString());
let paraArray = [];
let stmt = "CALL " + procName + "(";
for (let param in params) {
stmt = stmt + "?, ";
paraArray.push(params[param]);
}
stmt = stmt.substring(0, stmt.length - 2) + ")";
poolCluster.getConnection((err, connection) => {
connection.query(stmt, paraArray, function (err, rows, fields) {
if (err) {
throw err;
}
handler(res, rows);
});
connection.release();
});
}
)
}
};
配置文件如下:
主配置文件:
mysql: {
host: "127.0.0.1",
port: 3306,
user: "root",
password: "root",
database: "db_test",
charset: "utf8"
},
mysqlPool: {
acquireTimeout: 10000,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
},
mysqlPoolCluster: {
canRetry: true,
removeNodeErrorCount: 5,
restoreNodeTimeout: 0,
defaultSelector: "RR"
}
集群节点配置文件:
var settings = require("./settings");
module.exports = {
node01: {
host: '192.168.1.240',
port: settings.mysql.port,
database: settings.mysql.database,
user: settings.mysql.user,
password: "root",
charset: settings.mysql.charset,
acquireTimeout: settings.mysqlPool.acquireTimeout,
waitForConnections: settings.mysqlPool.waitForConnections,
connectionLimit: settings.mysqlPool.connectionLimit,
queueLimit: settings.mysqlPool.queueLimit
},
node02: {
host: "192.168.1.122",
port: settings.mysql.port,
database: settings.mysql.database,
user: settings.mysql.user,
password: "root",
charset: settings.mysql.charset,
acquireTimeout: settings.mysqlPool.acquireTimeout,
waitForConnections: settings.mysqlPool.waitForConnections,
connectionLimit: settings.mysqlPool.connectionLimit,
queueLimit: settings.mysqlPool.queueLimit
},
node03: {
host: '192.168.1.105',
port: settings.mysql.port,
database: settings.mysql.database,
user: settings.mysql.user,
password: "root",
charset: settings.mysql.charset,
acquireTimeout: settings.mysqlPool.acquireTimeout,
waitForConnections: settings.mysqlPool.waitForConnections,
connectionLimit: settings.mysqlPool.connectionLimit,
queueLimit: settings.mysqlPool.queueLimit
}
};