druid源码浅析

news/2025/1/23 0:56:00/

目录

配置

DruidDataSourceAutoConfigure

new DruidDataSourceWrapper()

DruidDataSource构造函数

init方法

 getConnection()

小结


配置

DruidDataSourceAutoConfigure

// 配置类
@Configuration
// 有DruidDataSource类时生效
@ConditionalOnClass(DruidDataSource.class)
// 在DataSourceAutoConfiguration类之前进行配置
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
// 读取配置
// DruidStatProperties主要是监控网站配置和网站计数过滤器配置
// DataSourceProperties 主要是数据库配置
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
// 导入四个不同的配置类
// DruidSpringAopConfiguration用于启用对 Druid 数据库连接池的 AOP 支持
// DruidStatViewServletConfiguration 根据配置开启Druid 连接池的监控统计功能
// DruidWebStatFilterConfiguration 根据配置开启Web和Druid数据源之间的管理关联监控统计
// DruidFilterConfiguration 根据配置注入各种filter的bean
@Import({DruidSpringAopConfiguration.class,DruidStatViewServletConfiguration.class,DruidWebStatFilterConfiguration.class,DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);// 初始化时执行DruidDataSource的init方法@Bean(initMethod = "init")@ConditionalOnMissingBeanpublic DataSource dataSource() {LOGGER.info("Init DruidDataSource");// 返回DruidDataSourceWrapper实例return new DruidDataSourceWrapper();}
}

new DruidDataSourceWrapper()

// 读spring.datasource.druid开头的配置
@ConfigurationProperties("spring.datasource.druid")
@Overridepublic void afterPropertiesSet() throws Exception {//如果不存在spring.datasource.druid的配置,就用'spring.datasource'配置if (super.getUsername() == null) {super.setUsername(basicProperties.determineUsername());}if (super.getPassword() == null) {super.setPassword(basicProperties.determinePassword());}if (super.getUrl() == null) {super.setUrl(basicProperties.determineUrl());}if (super.getDriverClassName() == null) {super.setDriverClassName(basicProperties.getDriverClassName());}}

DruidDataSource构造函数

DruidDataSource是DruidDataSourceWrapper的父类

实例化DruidDataSource时调用configFromPropety方法

方法主要是从System.getProperties()中读取配置,然后赋值,代码简单不再贴出。

需要注意的是,先执行configFromPropety方法,再执行set方法,配置的先后顺序是配置文件优先,也就是说同时使用System的Properties配置和配置文件配置,最后配置文件的配置会生效。

init方法

上边提到,实例化完dataSource的bean之后会执行DruidDataSource的init方法,让我们看看init方法做了什么

 // 判断是否被初始化过,是则直接退出  if (inited) {return;}
        DruidDriver.getInstance();final ReentrantLock lock = this.lock;try {// 尝试获取锁,如果获取失败则等待锁被释放,直到被其他线程打断或者当前线程获取到锁为止lock.lockInterruptibly();} catch (InterruptedException e) {throw new SQLException("interrupt", e);}
            // 为数据源分配idthis.id = DruidDriver.createDataSourceId();if (this.id > 1) {// 更新一些JMX的监控指标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();// 以下两个方法是解析url,从中找到配置去配置druidDataSource,包括filter、connectTimeout等initFromWrapDriverUrl();initFromUrlOrProperties();}
// 中间省略一些赋值操作
…… ……
// 从SPI ServiceLoader加载过滤器
initFromSPIServiceLoader()
// 给driver赋值
resolveDriver();
// 对dbType为Oracle、db2、mysql的做出相应处理
initCheck();
            // 根据driverClassName给exceptionSorter赋值,异常分类器可以帮助我们对不同类型的 SQL 异常进行分类和管理,例如识别数据库链接超时、死锁等情况,并进行相应的处理,避免出现严重的数据问题。initExceptionSorter();// 根据driverClassName给validConnectionChecker赋值,通过合法连接检测器,我们可以过滤掉已关闭或已失效的数据库连接,以避免在数据库操作时出现异常情况。initValidConnectionChecker();// 在 Druid 数据源内部,默认会使用 Validation Query 来检查每个数据库连接的有效性,以确保连接池中的所有连接都是可用的,从而提高数据库操作的效率和稳定性。validationQueryCheck();
         // 看是否配置全局数据源统计,dataSourceStat主要用于收集和记录数据源的性能指标和运行状态信息。通过 JdbcDataSourceStat 类的属性可以了解到数据源运行期间的各种指标信息,//并根据这些信息进行优化和调整,以提高数据源的性能和稳定性。同时,我们也可以通过 Druid 的 Web 控制台对数据源进行实时监控和管理,及时发现和解决问题。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 {dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbTypeName, this.connectProperties);}dataSourceStat.setResetStatEnable(this.resetStatEnable);
            // 声明了三个类型均为 DruidConnectionHolder 的数组// connections 数组表示数据源中所有的有效连接,当客户端请求连接时,连接池会向该数组中获取一个空闲连接,供客户端使用。// evictConnections 数组表示要被释放的无效连接,当检查到连接无效时,连接池会将该连接放入该数组中,并在后续时间将这些无效连接关闭和移除,以保证连接池中只有有效可用的连接。// keepAliveConnections 数组表示连接池中的存活连接,当连接池对连接进行健康检查时,发现连接处于“存活”状态,则将其放置在该数组中继续使用。connections = new DruidConnectionHolder[maxActive];evictConnections = new DruidConnectionHolder[maxActive];keepAliveConnections = new DruidConnectionHolder[maxActive];
            // 判断创建连接池的调度程序是否为空和asyncInit参数// 满足条件则异步创建连接if (createScheduler != null && asyncInit) {for (int i = 0; i < initialSize; ++i) {submitCreateTask(true);}} else if (!asyncInit) {// init connectionswhile (poolingCount < initialSize) {try {// 同步创建连接PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);// 赋值给上边初始化过的连接数组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();}}
            // 启动三个线程来完成连接池的维护过程// 该线程会每隔一段时间打印一次日志,用于记录连接池的运行状态和相关指标createAndLogThread();// 该线程会监测连接池中的连接数量,如果连接数小于最小活跃连接数,则会异步地创建新的连接并添加到连接池中,以保证连接池中至少存在最小活跃连接数的连接createAndStartCreatorThread();// 该线程会监测连接池中的空闲连接数量,如果连接数超出了最大空闲连接数,则会异步地销毁多余的连接,以避免连接池中的无效连接过多导致性能下降createAndStartDestroyThread();// 保证createAndStartCreatorThread完成才继续往下走initedLatch.await();
// 用于向 MBean 服务器注册 Druid 数据源的监控信息
// MBean(管理 Bean)是 Java 管理扩展(Java Management Extension,JMX)的核心概念之一,它是一种管理和监测 Java 应用程序的标准化方式。
// 创建一个 MBeanServer 对象,然后调用该对象的 registerMBean() 方法将 Druid 数据源的状态信息和配置信息注册为一个 MBean,即可在 JMX 控制台上查看和管理
registerMbean();
            // 如果设置了keepalive,连接会自动保持活动状态,以减少重新创建连接和验证连接的消耗if (keepAlive) {// 分为同步创建和异步创建连接if (createScheduler != null) {for (int i = 0; i < minIdle; ++i) {submitCreateTask(true);}} else {this.emptySignal();}}

 getConnection()

上边主要是初始的设置,接下来是获取连接的操作。

    public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {// 生成bean时已经初始化过,不再初始化init();// 如果过滤器不为空,则获取连接时对连接做一系列处理// 如果没使用过滤器则世界获取连接if (filters.size() > 0) {FilterChainImpl filterChain = new FilterChainImpl(this);return filterChain.dataSource_connect(this, maxWaitMillis);} else {return getConnectionDirect(maxWaitMillis);}}

 找到getConnectionInternal方法里的

                // 如果设置最大等待时间并且大于0if (maxWait > 0) {holder = pollLast(nanos);} else {// 如果最大等待时间小于0holder = takeLast();}
    DruidConnectionHolder takeLast() throws InterruptedException, SQLException {try {    while (poolingCount == 0) {emptySignal(); // 唤醒init里创建连接线程CreateConnectionThread…… ……try {notEmpty.await(); // 防止未初始化完成就往下走,初始化一个连接后唤醒这里} finally {notEmptyWaitThreadCount--;}…… ……}} catch (InterruptedException ie) {…… ……}// poolingCount--decrementPoolingCount();// 取出链接并把当前位置的connections置空DruidConnectionHolder last = connections[poolingCount];connections[poolingCount] = null;return last;}

 当一个连接使用完成之后,会调用DruidPooledConnection的close方法,然后调用DruidDataSource的recycle方法最后调用putLast(holder, currentTimeMillis)方法把连接放回到connections里边

小结

先简单分析到这里,日后遇到问题,再具体问题具体分析


http://www.ppmy.cn/news/175496.html

相关文章

java远程调用接口、URL的方式

一&#xff1a;httpUrlConnection 1.获取HttpURLConnection连接对象 /*** 获取HttpURLConnection连接对象* param url 远程调用的url* return*/public static HttpURLConnection getHttpURLConnection(String url){try {//建立连接URL httpUrl new URL(url);HttpURLConnectio…

MySQL 5.7 修改账号密码

MySQL 5.7 修改账号密码 1、概述2、更改密码2.1、寻找命令2.2、补充 3、总结 1、概述 大家好&#xff0c;我是欧阳方超。 MySQL数据库安装后设置的密码太简单了&#xff0c; 近期安全检查&#xff0c;这种弱密码全部得修改&#xff0c;好吧那就开始改吧 2、更改密码 2.1、寻…

台式计算机规格型号怎么查,台式电脑主板型号在哪里看

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 查看台式电脑主板型号的方法&#xff1a; 1、打开桌面左下角的“开始菜单”&#xff0c;在搜索功能中输入“DxDiag”然后点击运行。 2、接下来就会出现“DirectX诊断工具”&#xf…

dell台式计算机主板电池,台式机主板电池怎么拆

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 以戴尔台式机为例&#xff0c;台式机主板电池的拆法&#xff1a; 1、关闭电源&#xff0c;将所有插在机箱上面的电线与相关设备移除。 2、用十字的螺丝刀启开电脑机箱&#xff0c;将…

hp 服务器主板如何查看型号,hp台式电脑主板型号怎么查看

惠普ProDesk400G1SFFJ4J37PA惠普ProDesk400G1SFFJ4J37PA类型&#xff1a;商用台式机&#xff1b;操作系统&#xff1a;Linux&#xff1b;处理器&#xff1a;IntelCeleronG18402.8GHz/L32M&#xff1b;内存大小&#xff1a;2GB&#xff1b;硬盘容量&#xff1a;500GB&#xff1b…

windows环境使用clion搭建redis5.0 redis6.0的源码阅读环境

1、下载cygwin https://cygwin.com/install.html 第一步选择从互联网安装 别放在C盘 选择直接连接 我这边选择的是163的节点 接下来&#xff0c;就是让我们选择要安装的东西&#xff0c;网上一般给的就是如下几个&#xff1a; gcc-core、gcc-g、make、gdb、binutils 一个个…

台式机计算机在哪里看,IT教程:台式电脑主板型号在哪里看

科技就如同电灯发出的光一样&#xff0c;点亮我们的世界&#xff0c;点亮我们的生活&#xff0c;这一段时间以来台式电脑主板型号在哪里看的消息络绎不绝是什么原因呢?接下来就让我们一起了解一下吧。 大家好&#xff0c;我是智能客服时间君&#xff0c;上述问题将由我为大家进…

台式计算机M丅BF是什么,台式机主板的 BIOS ID 代码

如果您有一台零售的英特尔台式机主板&#xff0c;您的电脑上的 BIOS ID 代码就可以确认。请按照以下步骤查找 BIOS ID 代码&#xff1a;在启动过程中&#xff0c;按F2进入 BIOS 设置。 在主菜单上&#xff0c;记下 BIOS 版本(该菜单上的第一个项目)。 按F10退出 BIOS 设置程序。…