所属文章:池化技术(一)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. gradle环境搭建

    最近我在尝试了解跨平台技术的发展,首先则是想到了cordova.环境配置过程中有依赖gradle,下面简单记录了在windos10系统下搭建gradle环境的过程. 什么是gradle Gradle是 ...

  2. .net core3.0部署Linux服务器 使用Docker容器和Nginx反代理教程

    本人刚接触.net core 由于公司项目需要部署在Linux上 近些日子学习和网上大面积搜教程 我在这给大家归拢归拢借鉴的教程做了套方案(我写的可以实现 但不一定是最好的 仅供参考) 我只用过cor ...

  3. 使用 docker 部署常用的开发环境

    使用 docker 部署常用的开发环境 Intro 使用 docker,很多环境可以借助 docker 去部署,没必要所有的环境都在本地安装,十分方便. 前段时间电脑之前返厂修了,回来之后所有的软件都 ...

  4. 12.2 中的Data Guard Standby 密码文件自动同步 (Doc ID 2307365.1)

    Data Guard Standby Automatic Password file Synchronization in 12.2 (Doc ID 2307365.1) APPLIES TO: Or ...

  5. linux/kali安装及更新源以及输入法等配置

    准备工作1.kali 镜像的下载,官网选择对应的版本下载https://www.kali.org/downloads/ 2.VMware的安装,同官网可下载 更新源总结#更新源gedit /etc/a ...

  6. linux用户组相关,密码相关,文件操作,和启动级别

    一.开机重启和用户切换 注意,当关机或重启前,都应当先执行一下sync指令,把内存的数据写入磁盘,防止数据丢失. shutdown命令 shutdown -h now :立即关机 shutdown - ...

  7. alter对话框处理:

    from selenium import webdriverd = webdriver.Firefox()d.get('file://C:\\我的代码\\selenium自动化测试\\alter.ht ...

  8. NFS文件系统及搭建NFS共享服务

    一.什么是文件系统? 文件系统是对一个存储设备上的数据和元数据进行组织的一种机制.文件系统是在一个磁盘(包括光盘.软盘.闪盘及其它存储设备)或分区上组织文件方式方法,常见文件系统如ext2.ext3. ...

  9. SpringBoot2.0 基础案例(15):配置MongoDB数据库,实现增删改查逻辑

    本文源码:GitHub·点这里 || GitEE·点这里 一.NoSQL简介 1.NoSQL 概念 NoSQL( Not Only SQL ),意即"不仅仅是SQL".对不同于传统 ...

  10. uni-app中onLoad不起作用

    最近开始使用uni-app,坑还是很多的 今天在使用onLoad是发现,页面上的onLoad方法是可以起作用的,但是组件中的onLoad方法并没有起作用 后来经过一番尝试后还是不行,看文档发现uni- ...