Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索

DruidCP源码阅读7 -- 回收连接过程 阅读详情

Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索


简介

在上文中探索了数据库连接池的获取,下面接着初步来探索下数据库连接的关闭,看看其中具体执行了那些操作

连接关闭

下面的具体的代码,相关的位置,加上了自己的注释:

    @Override
    public void close() throws SQLException {
        // 下面两段检查避免重复关闭
        if (this.disable) {
            return;
        }

        DruidConnectionHolder holder = this.holder;
        if (holder == null) {
            if (dupCloseLogEnable) {
                LOG.error("dup close");
            }
            return;
        }

        // 判断是否是一样的线程,不是则使用异步关闭
        DruidAbstractDataSource dataSource = holder.getDataSource();
        boolean isSameThread = this.getOwnerThread() == Thread.currentThread();

        if (!isSameThread) {
            dataSource.setAsyncCloseConnectionEnable(true);
        }

        if (dataSource.isAsyncCloseConnectionEnable()) {
            syncClose();
            return;
        }

        if (!CLOSING_UPDATER.compareAndSet(this, 0, 1)) {
            return;
        }

        try {
            // 连接关闭时的一些增强?
            for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
                listener.connectionClosed(new ConnectionEvent(this));
            }

            // Filter也能作用到连接关闭时?
            List<Filter> filters = dataSource.getProxyFilters();
            if (filters.size() > 0) {
                FilterChainImpl filterChain = new FilterChainImpl(dataSource);
                filterChain.dataSource_recycle(this);
            } else {
                recycle();
            }
        } finally {
            CLOSING_UPDATER.set(this, 0);
        }

        this.disable = true;
    }

    
    public void recycle() throws SQLException {
        if (this.disable) {
            return;
        }

        DruidConnectionHolder holder = this.holder;
        if (holder == null) {
            if (dupCloseLogEnable) {
                LOG.error("dup close");
            }
            return;
        }

        if (!this.abandoned) {
            DruidAbstractDataSource dataSource = holder.getDataSource();
            dataSource.recycle(this);
        }

        this.holder = null;
        conn = null;
        transactionInfo = null;
        closed = true;
    }

下面是具体的recycle()函数内容:

    /**
     * 回收连接
     */
    protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
        final DruidConnectionHolder holder = pooledConnection.holder;

        if (holder == null) {
            LOG.warn("connectionHolder is null");
            return;
        }

        // 一个warn,并没有直接返回
        if (logDifferentThread //
            && (!isAsyncCloseConnectionEnable()) //
            && pooledConnection.ownerThread != Thread.currentThread()//
        ) {
            LOG.warn("get/close not same thread");
        }

        final Connection physicalConnection = holder.conn;

        // 使用双重检查锁,将traceEnable,目前尚不清楚其具体作用
        if (pooledConnection.traceEnable) {
            Object oldInfo = null;
            activeConnectionLock.lock();
            try {
                if (pooledConnection.traceEnable) {
                    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());
                }
            }
        }

        final boolean isAutoCommit = holder.underlyingAutoCommit;
        final boolean isReadOnly = holder.underlyingReadOnly;
        final boolean testOnReturn = this.testOnReturn;

        try {
            // check need to rollback?
            if ((!isAutoCommit) && (!isReadOnly)) {
                pooledConnection.rollback();
            }

            // 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();
            }

            if (holder.discard) {
                return;
            }

            // 如果计数标识大于配置的最大数,则关闭连接
            if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
                // 关闭了连接
                // 同时如果活跃连接数小于配置的最小活跃数,会发送通知(具体作用后面再查看)
                discardConnection(holder);
                return;
            }

            // 计数标识设置
            if (physicalConnection.isClosed()) {
                lock.lock();
                try {
                    if (holder.active) {
                        activeCount--;
                        holder.active = false;
                    }
                    closeCount++;
                } finally {
                    lock.unlock();
                }
                return;
            }

            // 检测连接是否还有效,无效则返还,并设置计数标识位
            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;
                }
            }
            if (holder.initSchema != null) {
                holder.conn.setSchema(holder.initSchema);
                holder.initSchema = null;
            }

            // 这里又关闭了一次连接
            // 在非enable的情况下
            if (!enable) {
                discardConnection(holder);
                return;
            }

            boolean result;
            final long currentTimeMillis = System.currentTimeMillis();

            // 连接超时关闭?这个超时具体是指什么还没搞清楚
            if (phyTimeoutMillis > 0) {
                long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
                if (phyConnectTimeMillis > phyTimeoutMillis) {
                    discardConnection(holder);
                    return;
                }
            }

            // 设置计数标识,同时没有关闭连接,而是放入池中
            lock.lock();
            try {
                if (holder.active) {
                    activeCount--;
                    holder.active = false;
                }
                closeCount++;

                result = putLast(holder, currentTimeMillis);
                recycleCount++;
            } finally {
                lock.unlock();
            }

            if (!result) {
                JdbcUtils.close(holder.conn);
                LOG.info("connection recyle failed.");
            }
        } catch (Throwable e) {
            // 异常情况下关闭连接
            holder.clearStatementCache();

            if (!holder.discard) {
                discardConnection(holder);
                holder.discard = true;
            }

            LOG.error("recyle error", e);
            recycleErrorCountUpdater.incrementAndGet(this);
        }
    }

总结

通过上面代码可以看出:

  • 1.Close做了同步和异步关闭之类的检查,但AsyncClose和recycle没有有比较大的区别,似乎也不是新开的线程去回收连接,看着似乎也是走的同步,这个目前对async的异步函数有点疑惑
  • 2.在回收连接的处理中,如果超过一定的条件限制,就会关闭连接;其他就会放回到连接池中
Spring Boot 3生产环境数据库密码加密实战:Jasypt+Druid配置详解 在软件工程实践中,配置管理是保障应用安全的重要环节,尤其是数据库密码等敏感信息。对称加密技术通过使用同一密钥进行加密和解密,为配置文件中的敏感数据提供了基础保护层。其技术价值在于防止明文密码泄露,满足生产环境的安全基线要求。应用场景广泛覆盖Spring Boot项目的多环境部署,如开发、测试和生产环境。本文聚焦于Spring Boot 3框架,结合Druid连接池与Jasypt加密库,详细解析如何实现application.yml中数据库密码的自动解密,并深入探讨密钥通过环境变量管理的安全策略,以及Dock 阅读详情

相关推荐

Druid 1.2.8数据库连接池:优化、监控与实战

本文还有配套的精品资源,点击获取 简介:Druid是阿里巴巴开源的高性能数据库连接池,支持多种数据库,具有强大的监控和管理功能。1.2.8版本引入多项改进,提高性能和稳定性。包括优化连接池算法、监控数据库连接状态、支持SQL解析和提供过滤器机制。此版本也修复已知bug,改善对主流数据库的兼容性,并提供详尽文档。压缩包提供了Javadoc、运行库和源码文件,便于开发者学习和集...

weixin_30533301的博客 3065

druid监控无法关闭(坑),及处理方式

更为详细的配置信息可以查看:https://github.com/brettwooldridge/HikariCP。以上方法试过均无效,然后再源码中看到了一些东西。

小肥宅的博客 1万+

druid(德鲁伊)-1.2.8连接池jar包

2021年10月最新发布,阿里druid-1.2.8连接池jar包,大项目亲测可用,欢迎下载

使用连接池技术后,是否需要手动关闭连接?

参考:https://blog.youkuaiyun.com/sunmaolin5003/article/details/84010014 参考:https://bbs.youkuaiyun.com/topics/270074065 ...

cihuan0804的博客 4604

druid1.2.8 下载 数据库连接池

druid1.2.8 下载 数据库连接池

AI虚拟人物 数字人直播,不用出镜,不用露脸的直播方式(附教程 软件)

我们先来看一组数据,这是我通过蝉妈妈查到的某位虚拟主播的一个带货数据,粉丝还不到 3000 人,每次直播 1 到 4 个小时,30天的总销量达到了1200单,15天,总销售额 2 万,可能相对来说客单价还是比较低,最关键的是才直播了 6 场,如果说他能够一个月直播 20 天,每天花 4个小时去直播,当个副业来做,就可以达到最起码月入 3 万的一个收入水平,我个人觉得是非常的不错的。大家把商品图片放在背景,只要场景设置的不错,场景和产品的关联度高,直播的时候挂上购买链接也会有一定的成交。

. 7万+

Druid关闭监控页面关闭不了

druid关闭监控页面关不掉

weixin_45614626的博客 4173

Blender - Shrinkwrap - 更方便的制作贴合模型的表面来建模

Shrinkwrap 修改器 使用 Blender 想要快速建模可以贴合现有模型表面的效果:衣服、皮肤、等效果,可以使用 Shrinkwrap 修改器,这个修改器的名字其实按照英文上的理解,翻译为:收缩的包裹效果 应用 如这块铁皮,它要贴合机身弯曲的效果,可以先建好一个铁皮,再 Shrinkwrap 到一个指定的网格上 留意的参数 Target 要贴合的对象(目前我已知是可以贴合到网格对象上,实在是方便) VertexGroup 顶点组,和之前讲到过的,顶点组可以组设顶点的权重,意思这个可.

Jave.Lin 的学习笔记 6068

Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索

在上篇文章中,了解了连接池的应用场景和本地运行了示例,本篇文章中,我们尝试来探索下Alibaba Druid数据库连接池的整体实现思路

github_35735591的博客 530

软件破解逆向安全(一)初识内存地址和偏移

工具:CE 内存:数字->存访在电脑中->内存 实例:如何修改植物大战僵尸阳光 内存搜索最核心:过滤数据 先用CE加载植物大战僵尸,如下图 我们可以看到目前我们的阳光数是50,我们尝试在ce里搜索精确数值,4 byte是int整数的表示。 我们发现有很多数据,尝试收集一个阳光变成75再次搜索: 我们发现就一个了,这时我们可以双击Value随便修改阳光的数值了。 这里我们说一下动态和静态地址 动态地址:临时用于存放数据的地址,黑色。特征:重新启动游戏地..

weixin_43781139的博客 2587

Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索

上文中分析了数据库连接池的初始化部分,接下来我们来看看获取连接部分的代码

github_35735591的博客 1459

Python爬虫实战:使用最新技术爬取GitHub Trending项目

本文将详细介绍如何使用Python最新技术栈构建一个高效的GitHub Trending爬虫。我们将使用异步请求库httpx、HTML解析库parsel、数据存储方案以及反反爬技术,完整实现一个能够稳定获取GitHub Trending数据的爬虫系统。文章包含5000余字详细教程,从环境搭建到代码实现,再到数据处理和存储,最后部署运行,提供全方位指导。GitHub Trending页面展示了GitHub上当前最受欢迎的项目,对于开发者了解技术趋势、发现优秀项目非常有价值。

2201_76125261的博客 735

Alibaba Druid 源码阅读(一) 数据库连接池初步

本文将初步探索数据库连接池的应用场景,为后面的源码分析做些准备

github_35735591的博客 378

yolov5-6.0的稀疏化训练、结构化剪枝、微调

YOLOv5的BN剪枝、微调

qq_41920323的博客 4394

1.4 你绝对不能错过的天气查询工具:MCP 标准化接入实战

本文介绍了如何构建一个符合MCP标准的天气查询服务。通过FastMCP框架集成OpenWeatherMap API,实现实时天气、天气预报和历史天气查询功能。文章详细展示了从环境准备、插件设计到控制器实现的完整流程,包括如何定义工具函数、配置路由和中间件。同时讲解了如何将MCP服务封装为LangChain工具并与DeepSeek等大模型集成,还涵盖了缓存、限流等高级特性。该示例演示了如何将任意API标准化为MCP服务,为Agent应用提供统一接口。

yonggeit的博客 3046

Druid 介绍及配置

Druid是Java领域优秀的数据库连接池,提供强大的监控和扩展功能。支持从Maven中央仓库下载,源码托管在GitHub。通过StatFilter可开启监控统计,内置监控页面需配置StatViewServlet。提供WallFilter防御SQL注入,支持多种日志记录和连接泄漏监测。Druid解决了Oracle的PSCache内存问题,支持JNDI配置,兼容DBCP迁移。每月发布周期稳定,支持主流数据库。特色功能包括自动识别DriverClass、慢SQL日志、密码加密等。开发者可通过GitHub参与项目

ywl470812087的博客 12万+

python实现带符号的二进制数转换十进制数 (附完整源码)

python实现带符号的二进制数转换十进制数 (附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 553

mysql 连接池泄露_一次 Druid 连接池泄露引发的血案!

最近某个应用程序老是卡,需要重启才能解决问题,导致被各种投诉,排查问题是 Druid 连接池泄露引发的血案。。异常日志如下:ERROR - com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 50, maxActive 50, creating 0at com.alibaba.druid.p...

weixin_39762348的博客 528

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

《Java 100 天进阶之路》第88篇:JDBC与连接池(2026版)

摘要:从 JDBC 基础到连接池调优,一篇打通 Java 数据库访问全链路。深度解析 Statement vs PreparedStatement 防注入原理,对比 HikariCP 与 Druid 选型与核心参数配置,结合 2026 年技术趋势,拓展虚拟线程 + 传统 JDBC、R2DBC 响应式双模架构,以及动态治理(Nacos/Apollo 联动)、可观测性(OpenTelemetry)、安全加固(环境变量 + K8s Secret)等企业级实践。附生产级配置模板与面试高频题,从“会用”到“精通”。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 5261

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

2022.05.10 Druid源码阅读——Druid预编译SQL的执行过程

Druid预编译SQL

风灵玄的博客 1009

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

线上FullGC频繁排查-druid

线上FullGC频繁的排查问题

Rocky的博客 4万+

DruidCP源码阅读9 -- DruidDataSource问题整理1

1、连接如何创建的? 创建连接方法:com.alibaba.druid.pool.DruidAbstractDataSource#createPhysicalConnection() 一下三个地方会调用createPhysicalConnection去尝试创建物理连接 1-1、com.alibaba.druid.pool.DruidDataSource#init 同步创建时 com.alibaba.druid.pool.DruidDataSource#getConnection(long)第一次..

扬帆起航 806

Druid 配置_DruidDataSource参考配置

以下是一个参考的连接池配置: <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --...

weixin_34038652的博客 187

Druid 配置_配置WebStatFilter

https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_%E9%85%8D%E7%BD%AEWebStatFilter WebStatFilter用于采集web-jdbc关联监控的数据。 web.xml配置 <filter> <filter-name>D...

weixin_34208185的博客 248

Java与大模型结合实战:基于工作流引擎构建HR招聘智能体

工作流引擎是企业级应用实现业务流程自动化的核心技术,它通过定义、执行和监控一系列任务节点,确保复杂业务逻辑的可视化编排与可靠执行。其技术价值在于将业务规则与执行逻辑解耦,提升系统的可维护性与扩展性。在AI时代,大模型(LLM)为工作流注入了智能决策能力,使其从简单的流程自动化升级为能够理解、推理和生成的智能体(Agent)。这种结合尤其适用于需要多步骤决策与处理的垂直场景,例如人力资源领域的招聘流程。本文聚焦于利用Java生态中的Alibaba-Graph工作流引擎,集成大模型API,构建一个可处理简历解析

weixin_30892889的博客 426
上一篇: Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索
下一篇: 数据库连接池Demo(1)单线程初步
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值