Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Druid源码阅读7-DruidDataSource的recycle过程

Druid源码阅读7-DruidDataSource的recycle过程

作者头像
冬天里的懒猫
发布于 2021-11-18 07:06:24
发布于 2021-11-18 07:06:24
95000
代码可运行
举报
运行总次数:0
代码可运行

Druid中的Connection在使用之后,要进行回收,而回收连接的方法就是recucle方法。 回收的主要目的是将连接的状态清空/重置之后,放置到连接池的connections数组的尾部,然后发送连接池lock的notEmpty条件变量通知消息,让等待的消费者线程来获取连接。

1.回收过程

回收方法首先要做的是,判断回收线程是否为同一个线程。如果不是,则打印日志输出。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (logDifferentThread //
    && (!isAsyncCloseConnectionEnable()) //
    && pooledConnection.ownerThread != Thread.currentThread()//
) {
    LOG.warn("get/close not same thread");
}

这个日志级别是warn,这是我们在使用连接池的过程中特别要注意的地方,一般来说,只有连接池发生了连接泄露,使用连接的线程长期持有连接而不执行具体操作,连接泄漏监测的线程才会来关闭连接。这种情况需要特别注意。 此外,连接泄露监测机制的removeAbandoned机制也会调用recycle方法来进行回收。

pooledConnection.traceEnable与activeConnections都是与removeAbandoned机制相关的参数,这将在后续关于removeAbandoned机制的时候详细分析。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (pooledConnection.traceEnable) {
    Object oldInfo = null;
    activeConnectionLock.lock();
    try {
        if (pooledConnection.traceEnable) {
            //将连接从activeConnections中移除 考虑到多线程场景,要加锁
            oldInfo = activeConnections.remove(pooledConnection);
            pooledConnection.traceEnable = false;
        }
    } finally {
        activeConnectionLock.unlock();
    }
    if (oldInfo == null) {
        if (LOG.isWarnEnabled()) {
            LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
        }
    }
}

rollback处理:如果不是自动commit切不是只读的连接,在回收的时候,先进行回滚操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// check need to rollback?
if ((!isAutoCommit) && (!isReadOnly)) {
    pooledConnection.rollback();
}

reset处理:此处需要判断是否为多线程场景,如果不是同一个线程,因为连接本身的线程有可能调用完毕之后释放的时候也会调用recycle,因此这个地方需要考虑到多线程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// reset holder, restore default settings, clear warnings
boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread();
if (!isSameThread) {
    final ReentrantLock lock = pooledConnection.lock;
    lock.lock();
    try {
        holder.reset();
    } finally {
        lock.unlock();
    }
} else {
    holder.reset();
}

最关键的部分是reset方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//清空Listeners
connectionEventListeners.clear();
statementEventListeners.clear();

lock.lock();
try {
    for (Object item : statementTrace.toArray()) {
        Statement stmt = (Statement) item;
        //关闭statement
        JdbcUtils.close(stmt);
    }
    //清空statementTrace
    statementTrace.clear();
} finally {
    lock.unlock();
}
//清空warning信息
conn.clearWarnings();

reset方法将connection中的statement都关闭,并将相关的报警灯内容清空。

关闭连接处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//如果状态为discard 则直接退出
if (holder.discard) {
    return;
}
//如果超过连接最大的使用次数,那么也将关闭连接
if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
    discardConnection(holder);
    return;
}

这个地方如果状态为udiscard,则直接退出recycle方法。会在后续的方法中被discard。 如果达到最大调用次数,也会关闭连接。

关闭清理: 如果连接已经关闭,则加锁,减去计数器即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (physicalConnection.isClosed()) {
        lock.lock();
        try {
            if (holder.active) {
                activeCount--;
                holder.active = false;
            }
            closeCount++;
        } finally {
            lock.unlock();
        }
        return;
    }

testOnReturn处理: 如果开启了testOnReturn,则发送测试数据,如果测试失败,则关闭连接。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (testOnReturn) {
    boolean validate = testConnectionInternal(holder, physicalConnection);
    if (!validate) {
        JdbcUtils.close(physicalConnection);

        destroyCountUpdater.incrementAndGet(this);

        lock.lock();
        try {
            if (holder.active) {
                activeCount--;
                holder.active = false;
            }
            closeCount++;
        } finally {
            lock.unlock();
        }
        return;
    }
}

如果配置了druid.phyTimeoutMillis,那么回收的时候需要对连接进行超时检测:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (phyTimeoutMillis > 0) {
    long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
    if (phyConnectTimeMillis > phyTimeoutMillis) {
        discardConnection(holder);
        return;
    }
}

最关键的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
lock.lock();
try {
    //修改active状态和activeCount计数器
    if (holder.active) {
        activeCount--;
        holder.active = false;
    }
    //增加closeCount计数器
    closeCount++;
    //将连接放置到数组的末尾
    result = putLast(holder, currentTimeMillis);
    recycleCount++;
} finally {
    lock.unlock();
}

这是连接回收的核心代码,就是将连接放置到数组的末尾。 在putLast方法中会调用 notEmpty.signal();这样消费者线程就会产生调用。

2.recycle的调用时机

recycle方法将在连接的close方法中被调用。在需要关闭连接的时候,调用recycle,将符合规则的连接添加到连接池的末尾。 调用recycle的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public void close() throws SQLException ;
  
  public void syncClose() throws SQLException ;

close和syncClose方法都会调用recycle。 在关闭连接之前,跟获取连接一样,都要进行责任链模式的filter处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (filters.size() > 0) {
    FilterChainImpl filterChain = new FilterChainImpl(dataSource);
    filterChain.dataSource_recycle(this);
} else {
    recycle();
}

这个过程与getConnection方法执行filter的过程类似。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/11/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
聊聊druid的return行为
com/alibaba/druid/pool/DruidPooledConnection.java
code4it
2023/09/26
2460
聊聊druid的borrow行为
com/alibaba/druid/pool/DruidDataSource.java
code4it
2023/09/25
3640
Druid源码阅读5-DruidDataSource的shrink过程
之后,要判断初始化状态是否完成,如果采用异步初始化,可能DestoryTask线程已经启动,但是连接池还没有初始化完成。
冬天里的懒猫
2021/11/15
8250
Druid源码阅读4-DruidDataSource的getConnection过程
DruidDataSource连接池实现了javaX.sql包中,DataSource接口的全部方法。getConnection也来自于javaX.sql.DataSource接口。 而DruidPooledConnection也实现了接口java.sql.Connection。 这样就能在各种场景中通过这个接口来获取数据库连接。
冬天里的懒猫
2021/11/15
1.6K0
聊聊druid的handleException
com/alibaba/druid/pool/DruidPooledConnection.java
code4it
2023/09/27
2770
Druid连接池原理学习
(1)、 如果设置了maxWait或者构造函数参数传入的为true,则创建的ReentrantLock为公平锁,否者为非公平锁 (2)、 如果设置了initialSize>=1,则会启动是创建initialSize个数数据库物理连接到线程池。 (3)、 如果没设置createScheduler则创建并启动数据库连接创建线程,如果没设置destroyScheduler则创建并启动数据库连接回收线程,如果timeBetweenLogStatsMillis>0则创建logstat线程。
加多
2018/09/06
6K0
Druid连接池原理学习
Druid源码阅读9-DruidDataSource和DruidConnection中的状态
DruidPooledConnection中的状态: 字段 类型 所在类 默认值 说明 closed volatile boolean DruidPooledConnection false 关闭状态,recycle到连接池中的连接会修改为true。但是这个状态通常只在checkStateInternal中单独使用。判断连接是否关闭需要结合(closed or disable) disable volatile boolean DruidPooledConnection false 不可用状态,当连接出现异
冬天里的懒猫
2021/11/22
8370
Druid源码阅读9-DruidDataSource和DruidConnection中的状态
Druid源码阅读8-DruidDataSource的removeAbandoned机制
有关于Druid的removeAbandoned机制,在getConnection源码中介绍过。removeAbandoned实际上就是Druid的泄露检测机制。主要的参数有:
冬天里的懒猫
2021/11/19
2.5K0
Druid源码阅读10-DruidDataSource中的一些计数器
在Druid连接池的工作过程中,会用到一些计数器对Druid的情况进行判断。然后根据计数器的数据采取一系列操作,整理如下:
冬天里的懒猫
2021/11/24
1.5K0
Druid源码阅读10-DruidDataSource中的一些计数器
池化技术有多牛?来,告诉你阿里的Druid为啥如此牛逼!
今天分享一篇非常不错的Druid原理解析的文章,对于Druid的整体流程分析的非常细致~
码猿技术专栏
2023/05/01
1.3K0
池化技术有多牛?来,告诉你阿里的Druid为啥如此牛逼!
聊聊druid的keepalive机制
com/alibaba/druid/pool/DruidDataSource.java
code4it
2023/10/01
3690
Druid源码阅读3-DruidDataSource连接池的基本原理
DruidDataSource数据库连接池的的本质,实际上是一个利用ReentrentLock和两个Condition组成的生产者和消费者模型。
冬天里的懒猫
2021/11/15
1.5K0
聊聊druid的DestroyConnectionThread
druid-1.2.11-sources.jar!/com/alibaba/druid/pool/DruidDataSource.java
code4it
2023/09/30
3000
Druid数据库连接池支持闲时检测吗?
从很早以前,我维护的一个应用在做数据源主备切换时,经常出现切换后获取连接超时。前些时候,我对这个问题做了深入的分析,并得到一些结论,在这里分享给大家。
zhangheng
2020/07/06
5K0
同事升级了MySQL驱动8.0,导致应用大量超时
最近同事发现新服务用的驱动都是 MySQL8.0,而一些老系统 MySQL 驱动版本较低,存在一些危险漏洞,于是主动的把一些老应用的 MySQL 驱动程序升级到了 8.0。然而升级后,在并发量较高时,查看监控打点,Druid 连接池拿到连接并执行 SQL 的时间大部分都超过 200ms。
业余草
2021/12/06
8590
同事升级了MySQL驱动8.0,导致应用大量超时
Druid连接报错的原因分析笔记
在使用druid的时候难免会有一些问题,之前就发现了这个问题。但是作者发现其对业务没有影响,所以打算拖一拖。主要是不影响业务,就会会频繁的报错。今天组内其他同学的业务的报错,然后也没定位到问题,最后反馈了druid连接池报错的问题,也就是上述作者说的对业务影响不大的那个报错,至于同事反映的真正的问题是什么,现在还没有复现。现在还是主要将druid的问题解决了再说吧,druid报错如下。
写一点笔记
2022/08/11
5.4K0
Druid连接报错的原因分析笔记
Mybatis源码初探——优雅精良的骨架
Mybatis是一款半自动的ORM框架,是目前国内Java web开发的主流ORM框架,因此作为一名开发者非常有必要掌握其实现原理,才能更好的解决我们开发中遇到的问题;同时,Mybatis的架构和源码也是很优雅的,使用了大量的设计模式实现解耦以及高扩展性,所以对其设计思想,我们也非常有必要好好理解掌握。(PS:本系列文章基于3.5.0版本分析)
夜勿语
2020/09/07
4880
Druid源码阅读2-DruidDataSource的init过程
DruidDataSource的使用都是创建DruidDataSource对象,set配置参数之后,调用init方法。 通过mock测试实例化DruidDataSource:
冬天里的懒猫
2021/11/11
1.4K0
Druid源码阅读2-DruidDataSource的init过程
golang redis 客户端源码阅读(2)连接池初始化
初始化连接池的核心代码如下 client.pool = &redis.Pool{ MaxIdle: client.MaxIdle, IdleTimeout: time.Duration(client.IdleTimeoutS) * time.Second, MaxActive: client.MaxActive, Dial: func() (redis.Conn, error) { var c redis.Conn var err e
golangLeetcode
2022/08/02
6800
源码阅读之CyclicBarrier
源码阅读是基于JDK7,本篇主要涉及CyclicBarrier常用方法源码分析。文中代码若格式排版不对,可点击底部的阅读原文阅读。 1.概述 CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(也可以叫同步点),即相互等待的线程都完成调用await方法,所有被屏障拦截的线程才会继续运行await方法后面的程序。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该屏障点在释放等待线程后可以重用,所以称它为循
JavaQ
2018/04/04
6390
相关推荐
聊聊druid的return行为
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档