一、数据源与连接池
前面我们说过,Mycat除了作为服务端外,还作为客户端来连接数据库,所以需要管理与数据库的连接。在管理后端连接中,主要涉及到下面两个类。
- PhysicalDatasource
BackendConnection
从下面的类图可以看出,同普通的数据源一样,PhysicalDatasource负责建立连接,并且内部维护了一个连接池——conMap。
Mycat目前提供了三种数据源的实现:MySQLDatasource、PostgreSQLDataSource、JDBCDatasource。其中,MySQLDatasource和PostgreSQLDataSource是通过NIO与MySQL通信,JDBCDatasource是通过传统的JDBC Driver(也就是BIO)与MySQL通信。也就是说,如果你的数据库用的是MySQL和PostgreSQL,那么后端通信用的是NIO,否则用的就是BIO。
二、获取后端连接
通过PhysicalDatasource的getConnection方法来获取连接时,返回值是void,这是因为Mycat后端的所有操作都是通过回调来完成的,当获取到连接后,会调用ResponseHandler的connectionAcquired()方法。
如果当前连接池中没有连接,就会异步创建新的连接,对于MySQL和PostgreSQL,通过工厂创建连接对象,注入一个SocketChannel实例。真正的创建过程交给NIOConnector处理;对于其他数据库,直接通过DriverManager来建立新的连接。
public void getConnection(String schema, boolean autocommit,
final ResponseHandler handler, final Object attachment)
throws IOException {
// 从当前连接map中拿取已建立好的后端连接
BackendConnection con = this.conMap.tryTakeCon(schema, autocommit);
if (con != null) {
//如果不为空,则绑定对应前端请求的handler
takeCon(con, handler, attachment, schema);
return;
} else {
int curTotalConnection = this.totalConnection.get();
synchronized (this) {
while (curTotalConnection + 1 <= size) {
if (this.totalConnection.compareAndSet(curTotalConnection, curTotalConnection + 1)) {
//没有空闲连接,创建新的连接
createNewConnection(handler, attachment, schema);
return;
}
//CAS更新失败,则重新判断当前连接是否超过最大连接数
curTotalConnection = this.totalConnection.get();
}
}
// 如果后端连接不足,立即失败,故直接抛出连接数超过最大连接异常
throw new IOException("the max activeConnnections size can not be max than maxconnections:" + curTotalConnection);
}
}
private BackendConnection takeCon(BackendConnection conn,
final ResponseHandler handler, final Object attachment,
String schema) {
conn.setBorrowed(true);
if (!conn.getSchema().equals(schema)) {
// need do schema syn in before sql send
conn.setSchema(schema);
}
ConQueue queue = conMap.getSchemaConQueue(schema);
queue.incExecuteCount();
conn.setAttachment(attachment);
// 每次取连接的时候,更新下lasttime,防止在前端连接检查的时候,关闭连接,导致sql执行失败
conn.setLastTime(System.currentTimeMillis());
incrementActiveCountSafe(conn);
handler.connectionAcquired(conn);
return conn;
}
private void createNewConnection(final ResponseHandler handler,
final Object attachment, final String schema) throws IOException {
// aysn create connection
MycatServer.getInstance().getBusinessExecutor().execute(new Runnable() {
public void run() {
try {
createNewConnection(new DelegateResponseHandler(handler) {
@Override
public void connectionError(Throwable e, BackendConnection conn) {
// 如果创建连接失败,将当前连接数减1
decrementTotalConnectionsSafe();
handler.connectionError(e, conn);
}
@Override
public void connectionAcquired(BackendConnection conn) {
takeCon(conn, handler, attachment, schema);
}
}, schema);
} catch (IOException e) {
handler.connectionError(e, null);
}
}
});
}
三、空闲连接心跳检测
首先需要明确下面几个概念:
- 总连接数=活跃连接数+空闲连接数
- 活跃连接数
- 空闲连接数
- 最小连接数
- 最小空闲连接数