一直想研究下Druid连接池底层的源码实现,正好今天有空,废话不说直接上源码。
今天先上最核心的连接池部分,至于后续的源码,后面会逐步安排上。
首先我们先来到源码的入口,我这里选用了test包中的,一个测试类。不得不说作者提供的测试类,还是挺多的。
通过上面的获取getConnection方法,可以直接进入DruidDataSource核心的init方法
init 方法上来:
(1)先初始化DruidDriver,在DruidDriver类被加载时,会进行了比较重要的两件事
//1.注册driveManager DriverManager.registerDriver(driver); //把driver注册到jmx上 mbeanServer.registerMBean(instance, objectName);
注册到jmx上就可以通过java提供的监控工具,在程序运行的时候,动态的监控,内部的属性值了, 也可以通过手动点击的方法,调用下列方法 监控的属性如下:
//JMX可以监控版本 String getDruidVersion(); //JMX中可以监控Druid的连接数 long getConnectCount(); //可以在JMX中手动调用 resetStat方法 void resetStat(); String getAcceptPrefix(); boolean jdbcCompliant(); int getMinorVersion(); int getMajorVersion(); // String[] getDataSourceUrls();
(2) 可以通过调整url连接的样式,完成过滤器的注入操作.配置的样式如下:
jdbc:wrap-jdbc:driver=rawClassName:filters= , :name=: jmx=true/false
其中:rawClassName 为驱动对应的className的类名。可以手动指定使用哪种驱动类.
filters= 后面就是配置过滤器参数的,多个参数用逗号隔开.
name表示数据源的名称
jmx:表示jdbcstat数据是否要监控
(3)如果外部配置了filter列表,在这个块会被初始化。通过这可以看出,可以在外部灵活的注入filters参数。
(4)initFromSPIServiceLoader: 该方法作用是提供了更灵活的方法来配置filter列表,允许通过SPI的机制,在META-INF的service文件夹下,配置filter列表,这边提供了一个关闭已配置的filter的功能,通过在Filter类上配置AutoLoad(value=false)的方式
(5)resolveDriver():应该叫createDrive,就是根据前几步得到,DriveClass类创建驱动.这里面还有对 其他的情况的适配
if (MockDriver.class.getName().equals(driverClass)) { driver = MockDriver.instance; } else if ("com.alibaba.druid.support.clickhouse.BalancedClickhouseDriver".equals(driverClass)) { Properties info = new Properties(); info.put("user", username); info.put("password", password); info.putAll(connectProperties); driver = new BalancedClickhouseDriver(jdbcUrl, info); }
(6)initcheck():确认当前的数据库的类型是mysql,oracle哪种类型
(7) initexceptionsort:里面封装了剔除无用连接的逻辑.就是通过这个类来判断哪些连接是无用的.该方法的作用是,根据数据库使用的类型,判断使用哪一种剔除器
(8)initValidConnectionChecker:初始化维护连接器,要想维护长连接,必须给mysql发送心跳包.通过MySqlValidConnectionChecker 这个类中的isValidConnection方法,类维护连接的有效性.
//判断是否使用全部的状态监控
if (isUseGlobalDataSourceStat()) {
dataSourceStat = JdbcDataSourceStat.getGlobal();
if (dataSourceStat == null) {
dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbTypeName);
JdbcDataSourceStat.setGlobal(dataSourceStat);
}
if (dataSourceStat.getDbType() == null) {
dataSourceStat.setDbType(this.dbTypeName);
}
} else {
//初始化JdbcDataSourceStat类,用来统计JdbcSqlStat的参数
dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbTypeName, this.connectProperties);
}
dataSourceStat.setResetStatEnable(this.resetStatEnable);
connections = new DruidConnectionHolder[maxActive];
evictConnections = new DruidConnectionHolder[maxActive];
keepAliveConnections = new DruidConnectionHolder[maxActive];
SQLException connectError = null;
//下面是对连接的初始化操作,提供了两个方式
(1).异步初始化操作
if (createScheduler != null && asyncInit) {
for (int i = 0; i < initialSize; ++i) {
submitCreateTask(true);
}
} else if (!asyncInit) {
(2)同步初始化操作
// init connections
while (poolingCount < initialSize) {
try {
//创建连接或者代理的连接.并且统计连接的各种时间
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
//用一个Holder类来包装连接
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
//把holder放入到连接池中
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}
if (poolingCount > 0) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
}
PhysicalConnectionInfo类中包含的属性 //物理连接,这个连接有可能是代理连接 private Connection connection; //连接开始的时间 private long connectStartNanos; //建立连接的时候 private long connectedNanos; //初始化参数的时间 private long initedNanos; //校验完成的时间 private long validatedNanos; //该库对应的参数 private Map<String, Object> vairiables; //全局对应的参数 private Map<String, Object> globalVairiables;
//后台创建线程 间隔一段时间,打印日志信息
createAndLogThread();
//当连接过多时,就停止创建,阻塞该线程
createAndStartCreatorThread();
//当连接小于minIdle的时候,就唤醒上的线程,创建连接
createAndStartDestroyThread();
initedLatch.await();
init = true;
initedTime = new Date();
registerMbean();
if (connectError != null && poolingCount == 0) {
throw connectError;
}
if (keepAlive) {
// async fill to minIdle
if (createScheduler != null) {
for (int i = 0; i < minIdle; ++i) {
submitCreateTask(true);
}
} else {
this.emptySignal();
}
}
2