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

1、回收连接大致流程

2、close

com.alibaba.druid.pool.DruidPooledConnection#close

DruidPooledConnection是对holder的更抽象的封装,目的是更方便的对连接进行操作

这里的close是判断是正常连接关闭还是监控线程介入关闭,两者的后续流程会不同。

public void close() throws SQLException {
        // DruidPooledConnection是对holder的在一次的抽象封装
        // 判断当前连接是否为丢弃状态,是的话则直接返回不操作关闭操作,在连接检测时会将这个连接驱逐出连接池
        if (this.disable) {
            return;
        }

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

        // 获取当前连接的dataSource,dataSource封装了当前连接的各种配置参数
        DruidAbstractDataSource dataSource = holder.getDataSource();

        // 判断当前对象封装的线程与执行线程是否为同一个线程
        boolean isSameThread = this.getOwnerThread() == Thread.currentThread();

        // 如果不是同一个线程,设置setAsyncCloseConnectionEnable为true
        if (!isSameThread) {
            dataSource.setAsyncCloseConnectionEnable(true);
        }

        /*
            会检测是否开启了removeAbandoned
            若开启后或者setAsyncCloseConnectionEnable为true 则会执行syncClose
            由于和当先获取连接的线程不是同一个线程所以syncClose里面会加锁
            若getConnection的线程在执行sql时发生了问题导致连接一直被占用,那么监控线程destroyTask线程就会介入并关闭这个连接
         */
        if (dataSource.isAsyncCloseConnectionEnable()) {
            syncClose();
            return;
        }

        // CAS 修改“closing” 成员变量,由于该变量并没有用Atomic封装,为了保证原子操作所以使用AtomicIntegerFildUpdater
        // 将closing由0 -> 1,标记当前连接正在关闭
        if (!CLOSING_UPDATER.compareAndSet(this, 0, 1)) {
            return;
        }

        try {
            for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
                listener.connectionClosed(new ConnectionEvent(this));
            }

            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;
    }

3、recycle 回收连接

先看DruidPooledConnection#recycle,这个对象的recycle方法

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

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

        // 没有开启removeAbandoned,则不会直接丢掉连接,而是进入dataSource.recycle阶段,将连接重新放入连接池
        if (!this.abandoned) {
            DruidAbstractDataSource dataSource = holder.getDataSource();
            dataSource.recycle(this);
        }

        // 断掉与holder的关联,监控线程不会走上面的回收流程,直接断掉
        this.holder = null;
        conn = null;
        transactionInfo = null;
        closed = true;
    }

回收连接 DruidDataSource#recycle,先关注最核心的代码块 putLast(),这里就是重新将连接放入连接池

   protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
        。。。

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

    boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
        // 可用连接数 > 池中支持的最大活连接数跃数 或者 discard标记为true 或者 连接已经关闭则不回收
        if (poolingCount >= maxActive || e.discard || this.closed) {
            return false;
        }

        // 标记当前连接的最后活跃时间为当前系统时间
        e.lastActiveTimeMillis = lastActiveTimeMillis;
        // 将这个holder封装的连接对象放入池中,并且 poolingCount++
        connections[poolingCount] = e;
        incrementPoolingCount();

        // 标记峰值
        if (poolingCount > poolingPeak) {
            poolingPeak = poolingCount;
            poolingPeakTime = lastActiveTimeMillis;
        }

        // 唤醒消费者 可以使用连接了
        notEmpty.signal();
        notEmptySignalCount++;

        return true;
    }

DruidDataSource#recycle回收连接只分析了连接放入连接池的操作,前面还有很多逻辑,holder.reset,事务的提交等等重要的逻辑

总结

Druid回收连接时会进行判断1、连接泄漏时直接丢掉连接;2、回收复用连接重新放入connections

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyc_2754

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值