所属文章:池化技术(一)Druid是如何管理数据库连接的?

本代码段对应主流程2,具体用来初始化整个连接池:


public void init() throws SQLException {
if (inited) {
return; //如果已经被初始化过,则终止该方法
} // bug fixed for dead lock, for issue #2980
DruidDriver.getInstance(); final ReentrantLock lock = this.lock; //获取重入锁
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupt", e);
} boolean init = false;
try {
if (inited) { //双重检查
return;
} initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); this.id = DruidDriver.createDataSourceId(); //生成连接池id
if (this.id > 1) {
//生成其他对象的id,比如连接对象的id、statement对象的id
long delta = (this.id - 1) * 100000;
this.connectionIdSeedUpdater.addAndGet(this, delta);
this.statementIdSeedUpdater.addAndGet(this, delta);
this.resultSetIdSeedUpdater.addAndGet(this, delta);
this.transactionIdSeedUpdater.addAndGet(this, delta);
} if (this.jdbcUrl != null) {
this.jdbcUrl = this.jdbcUrl.trim();
initFromWrapDriverUrl(); //jdbc url的头必须是jdbc:wrap-jdbc才会触发该方法里的逻辑(这个头貌似是oracle的?本篇文章仅针对mysql)
} for (Filter filter : filters) {
filter.init(this); //通过池对象初始化filters(因为filter里面可能会用到一些池属性)
} if (this.dbType == null || this.dbType.length() == 0) {
this.dbType = JdbcUtils.getDbType(jdbcUrl, null); //根据jdbc协议头分析出当前数据库的类型(本文默认mysql)
} if (JdbcConstants.MYSQL.equals(this.dbType)
|| JdbcConstants.MARIADB.equals(this.dbType)
|| JdbcConstants.ALIYUN_ADS.equals(this.dbType)) {
boolean cacheServerConfigurationSet = false;
if (this.connectProperties.containsKey("cacheServerConfiguration")) {
cacheServerConfigurationSet = true;
} else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
cacheServerConfigurationSet = true;
}
if (cacheServerConfigurationSet) {
this.connectProperties.put("cacheServerConfiguration", "true");
}
} //下面就是对设置的这些属性合理性的判断,不符合要求的将直接抛异常
if (maxActive <= 0) {
throw new IllegalArgumentException("illegal maxActive " + maxActive);
} if (maxActive < minIdle) {
throw new IllegalArgumentException("illegal maxActive " + maxActive);
} if (getInitialSize() > maxActive) {
throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
} if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
} if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
} if (this.driverClass != null) {
this.driverClass = driverClass.trim();
} //通过SPI机制加载责任链上需要执行的filter,方法详情在下面
initFromSPIServiceLoader(); //如果driver为空,加载驱动,最终将加载到的驱动注册到DriverManager上去
if (this.driver == null) {
if (this.driverClass == null || this.driverClass.isEmpty()) {
this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); //在driverClass不配置的情况下,druid会通过url来判定属于哪个driverClass
} if (MockDriver.class.getName().equals(driverClass)) { //忽略
driver = MockDriver.instance;
} else {
if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
throw new SQLException("url not set");
}
driver = JdbcUtils.createDriver(driverClassLoader, driverClass); //driverClass不为空的情况下直接触发驱动加载
}
} else { //除非手动设置驱动,否则不会走这里的逻辑
if (this.driverClass == null) {
this.driverClass = driver.getClass().getName();
}
} initCheck(); //根据dbType的不同,来初始化一些标记字段(比如isMySql) initExceptionSorter(); //异常处理器初始化
initValidConnectionChecker(); //初始化长连接检测时所需要用到的checker的适配类型,具体实现在下面
validationQueryCheck(); //简单的检测validationQuery参数是否填写了,若没填写会打印一个错误日志,不影响主流程 if (isUseGlobalDataSourceStat()) { //默认不开启,忽略
dataSourceStat = JdbcDataSourceStat.getGlobal();
if (dataSourceStat == null) {
dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType);
JdbcDataSourceStat.setGlobal(dataSourceStat);
}
if (dataSourceStat.getDbType() == null) {
dataSourceStat.setDbType(this.dbType);
}
} else {
dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties);
}
dataSourceStat.setResetStatEnable(this.resetStatEnable); //下面三个数组都跟池子本身有关系,所以容量为maxActive
connections = new DruidConnectionHolder[maxActive]; //初始化连接池本体
evictConnections = new DruidConnectionHolder[maxActive]; //初始化丢弃连接数组(流程4.1需要用到)
keepAliveConnections = new DruidConnectionHolder[maxActive]; //初始化需要检测可用性连接数组(流程4.1要用) SQLException connectError = null; if (createScheduler != null && asyncInit) { //另外一种通过线程池管理连接池的方式,默认不启用,忽略
for (int i = 0; i < initialSize; ++i) {
createTaskCount++;
CreateConnectionTask task = new CreateConnectionTask(true);
this.createSchedulerFuture = createScheduler.submit(task);
}
} else if (!asyncInit) {
// init connections
while (poolingCount < initialSize) { //当池子里的连接数少于需要初始化的个数时,则需要不断新增连接填充连接池,直到等于初始化连接数
try {
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); //直接通过驱动程序创建连接对象,参考流程2.1
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); //异常报错后会休眠3s来进行下次的添加
}
}
} if (poolingCount > 0) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
}
} createAndLogThread(); //开启打印log日志的守护线程
createAndStartCreatorThread(); //开启负责新增连接的守护线程(主流程3)
createAndStartDestroyThread(); //开启负责丢弃连接的守护线程(主流程4) initedLatch.await(); //倒计数器,用来保证上面的主流程3和4两个守护线程全部开启完毕后才进行接下来的操作
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) {
createTaskCount++;
CreateConnectionTask task = new CreateConnectionTask(true);
this.createSchedulerFuture = createScheduler.submit(task);
}
} else {
this.emptySignal(); //keepAlive=true,主动唤起主流程3一次
}
} } catch (SQLException e) {
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e;
} catch (InterruptedException e) {
throw new SQLException(e.getMessage(), e);
} catch (RuntimeException e){
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e;
} catch (Error e){
LOG.error("{dataSource-" + this.getID() + "} init error", e);
throw e; } finally {
inited = true; //初始化完成后置为true
lock.unlock(); //释放锁 if (init && LOG.isInfoEnabled()) {
String msg = "{dataSource-" + this.getID(); if (this.name != null && !this.name.isEmpty()) {
msg += ",";
msg += this.name;
} msg += "} inited"; LOG.info(msg);
}
}
} private void initFromSPIServiceLoader() {
if (loadSpifilterSkip) { //默认不跳过SPI加载
return;
} if (autoFilters == null) {
List filters = new ArrayList();
ServiceLoader autoFilterLoader = ServiceLoader.load(Filter.class); //加载Filter的实现 for (Filter filter : autoFilterLoader) {
AutoLoad autoLoad = filter.getClass().getAnnotation(AutoLoad.class);
if (autoLoad != null && autoLoad.value()) {
filters.add(filter);
}
}
autoFilters = filters;
} for (Filter filter : autoFilters) {
if (LOG.isInfoEnabled()) {
LOG.info("load filter from spi :" + filter.getClass().getName());
}
addFilter(filter); //把通过SPI机制加载到的filter放到池子的filters里,用于后续责任链触发
}
} private void initValidConnectionChecker() { //初始化checker
if (this.validConnectionChecker != null) {
return;
} String realDriverClassName = driver.getClass().getName(); //根据驱动的class名称,来适配具体的checker实现
if (JdbcUtils.isMySqlDriver(realDriverClassName)) {
this.validConnectionChecker = new MySqlValidConnectionChecker(); //假设是mysql类型的驱动,那么适配到mysql的checker,MySqlValidConnectionChecker的构造器参考下面的方法 } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)
|| realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) {
this.validConnectionChecker = new OracleValidConnectionChecker(); } else if (realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER)
|| realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_SQLJDBC4)
|| realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_JTDS)) {
this.validConnectionChecker = new MSSQLValidConnectionChecker(); } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER)
|| realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)) {
this.validConnectionChecker = new PGValidConnectionChecker();
}
} //Mysql对应的checker构造器
public MySqlValidConnectionChecker(){
try {
clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
if (clazz == null) {
clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
} if (clazz != null) {
//如果驱动程序本身有ping方法,则下面的usePingMethod设置为true,后续连接保活测试就会采用ping.invoke的方式触发。
ping = clazz.getMethod("pingInternal", boolean.class, int.class);
} if (ping != null) {
usePingMethod = true;
}
} catch (Exception e) {
LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e);
} configFromProperties(System.getProperties());
}

Druid-代码段-2-1的更多相关文章

  1. WPF自定义RoutedEvent事件代码段

    今天在写东西的时候,发现常用的代码段里没有RoutedEvent的,因此,写了一个代码段,方便以后使用,顺便记录一下,如何做代码段. 1.在项目中新建一个XML文件,将扩展名修改为snippet. 2 ...

  2. JavaScript代码段整理笔记系列(二)

    上篇介绍了15个常用代码段,本篇将把剩余的15个补齐,希望对大家有所帮助!!! 16.检测Shift.Alt.Ctrl键: event.shiftKey; //检测Shift event.altKey ...

  3. 我们为什么要看《超实用的Node.JS代码段》

    不知道自己Node.JS水平如何?看这张图 如果一半以上的你都不会,必须看这本书,一线工程师用代码和功能页面来告诉你每一个技巧点. 都会一点,但不知道如何检验自己,看看本书提供的面试题: 1.     ...

  4. 《超实用的JavaScript代码段》—— 读后总结

    这本书全是代码,从头到尾跟着坐下来确实收获很多.比那些古板的教科书式的理解更多,不过书中并不是每个例子都做了,有的作者封装的太多,觉得看了收获不多,就没细看——比如模块渐变.有空好好学学这段的代码. ...

  5. Visual Studio常用小技巧一:代码段+快捷键+插件=效率

    用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...

  6. 使用eclipse开发Morphline的Java代码段

    背景:morphline是一个轻量级的etl工具.除了提供标准化的方法之外,还可以定制化的开发java片段.定制化的java片段会在加载时被作为一个独立的类编译,对源数据作处理. morphline关 ...

  7. 前端福利!10个短小却超实用的JavaScript 代码段

    JavaScript正变得越来越流行,它已经成为前端开发的第一选择,并且利用基于JavaScript语言的NodeJS,我们也可以开发出高 性能的后端服务,甚至我还看到在硬件编程领域也出现了JavaS ...

  8. Visual C# 代码段

    代码段是现成的代码段,您可以快速将其插入到您的代码中. 例如,for 代码段创建一个空的 for 循环. 有些代码段为外侧代码段,这些代码段允许您先选择代码行,然后选择要并入选定代码行的代码段. 例如 ...

  9. 十五个常用的jquery代码段【转】

    好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...

  10. 十五个常用的jquery代码段

    十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...

随机推荐

  1. python网络爬虫之解析网页的BeautifulSoup(爬取电影图片)[三]

    目录 前言 一.BeautifulSoup的基本语法 二.爬取网页图片 扩展学习 后记 前言 本章同样是解析一个网页的结构信息 在上章内容中(python网络爬虫之解析网页的正则表达式(爬取4k动漫图 ...

  2. C# .NET的BinaryFormatter、protobuf-net、Newtonsoft.Json以及自己写的序列化方法序列化效率和序列化后的文件体积大小对比

    测试结果如下图: 测试结果整理后: 结论: 1.这几个工具中,protobuf-net序列化和反序列化效率是最快的 2.BinaryFormatter和Newtonsoft.Json反序列化慢的比较多 ...

  3. java后台树形结构展示---懒加载

    一.数据库设计 二.实体类:entity import com.joyoung.cloud.security.common.validatedGroup.Add;import com.joyoung. ...

  4. CentOS 安装nginx服务

    安装nginx服务 sudo yum install nginx 启动nginx systemctl start nginx 加入启动项 systemctl enable nginx 测试nginx服 ...

  5. 如何在Oracle 12C中添加多个分区 (Doc ID 1482456.1)

    How to Add Multiple Partitions in Oracle 12C (Doc ID 1482456.1) APPLIES TO: Oracle Database - Enterp ...

  6. 使用webstrom开发小程序要做的设置

    1.关闭rpx的错误提示 在setting里面  -->搜索inspections --> 在右侧找到invalid CSS property value    把对勾划掉

  7. java之List接口(单列集合)

    List接口概述 查询API我们可知:java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了 List 接口的对 象称为List集合.在Li ...

  8. HTTP常见响应状态码及解释、常用请求头及解释

    1.HTTP常见响应状态码及解释2XX Success(成功状态码) 200 表示从客户端发来的请求在服务器端被正常处理204 该状态码表示服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主 ...

  9. 【转载】【PAT】PAT甲级题型分类整理

    最短路径 Emergency (25)-PAT甲级真题(Dijkstra算法) Public Bike Management (30)-PAT甲级真题(Dijkstra + DFS) Travel P ...

  10. js基本操作

    js操作页面三步骤 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...