2022-05-18 Druid源码阅读——removeAbandoned参数(一)

1.removeAbandoned

removeAbandoned功能常用来进行连接泄露检查,该功能共有三个参数进行组合配置。

参数类型默认值含义
removeAbandonedBooleanfalse是否开启removeAbandoned功能
removeAbandonedTimeoutLong300000活动时间超过该参数值的连接将被自动回收
logAbandonedBooleanfalse是否开启日志打印回收信息

2.removeAbandoned功能如何实现自动回收长链接

removeAbandoned功能是由DruidDataSource#removeAbandoned方法进行处理,该方法将对存储于activeConnections中的连接进行遍历,通过对DruidPooledConnection中的相关状态进行跟踪来判断是否满足回收条件。

public int removeAbandoned() {
    //驱逐计数
    int removeCount = 0;
    //得到当前时间
    long currrentNanos = System.nanoTime();
    //创建List,用于记录需要被驱逐的连接
    List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();
    //加锁,避免出现线程问题
    activeConnectionLock.lock();
    try {
        //以迭代器的形式获得activeConnections的所有Key(即连接对象)
        Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();
        //遍历迭代器
        for (; iter.hasNext();) {
            //获得当前连接
            DruidPooledConnection pooledConnection = iter.next();
            //如果当前连接正在执行中,则暂不处理
            if (pooledConnection.isRunning()) {
                continue;
            }
            //判断该连接存在时间,取值为当前时间-创建连接时间/1000*1000(将纳秒转为毫秒)
            long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
            //如果连接存在时间超过了指定值
            if (timeMillis >= removeAbandonedTimeoutMillis) {
                //移除该连接
                iter.remove();
                //取消对该连接的跟踪
                pooledConnection.setTraceEnable(false);
                //添加被驱逐的连接
                abandonedList.add(pooledConnection);
            }
        }
    } finally {
        //解锁
        activeConnectionLock.unlock();
    }
    //如果存在被驱逐(待回收)的连接
    if (abandonedList.size() > 0) {
        //遍历列表
        for (DruidPooledConnection pooledConnection : abandonedList) {
            //连接加锁
            final ReentrantLock lock = pooledConnection.lock;
            lock.lock();
            try {
                //判断连接是否被禁用,被禁用的连接将被其他线程回收,这里不处理
                if (pooledConnection.isDisable()) {
                    continue;
                }
            } finally {
                //解锁
                lock.unlock();
            }
            //关闭与数据库的连接
            JdbcUtils.close(pooledConnection);
            //设置状态
            pooledConnection.abandond();
            //全局自动回收计数自增
            removeAbandonedCount++;
            //本次自动回收计数自增
            removeCount++;
            //如果开启了logAbandoned,则拼接日志并进行输出
            if (isLogAbandoned()) {
                StringBuilder buf = new StringBuilder();
                buf.append("abandon connection, owner thread: ");
                buf.append(pooledConnection.getOwnerThread().getName());
                buf.append(", connected at : ");
                buf.append(pooledConnection.getConnectedTimeMillis());
                buf.append(", open stackTrace\n");

                StackTraceElement[] trace = pooledConnection.getConnectStackTrace();
                for (int i = 0; i < trace.length; i++) {
                    buf.append("\tat ");
                    buf.append(trace[i].toString());
                    buf.append("\n");
                }

                buf.append("ownerThread current state is " + pooledConnection.getOwnerThread().getState()
                           + ", current stackTrace\n");
                trace = pooledConnection.getOwnerThread().getStackTrace();
                for (int i = 0; i < trace.length; i++) {
                    buf.append("\tat ");
                    buf.append(trace[i].toString());
                    buf.append("\n");
                }

                LOG.error(buf.toString());
            }
        }
    }
    //返回本次回收连接数
    return removeCount;
}

2.1 removeAbandoned如何判断连接正在执行中

removeAbandoned()方法中,存在pooledConnection.isRunning()判断用于判断连接是否正在执行中,该方法取值为连接对象中的一个内部状态,该状态标记了当前连接是否正在执行,这里以DruidPooledPreparedStatement#executeQuery()执行进行分析。


    @Override
    public ResultSet executeQuery() throws SQLException {
		···省略部分代码···
		//此处调用连接对象的beforeExecute()方法
        conn.beforeExecute();
        try {
            ResultSet rs = stmt.executeQuery();

            if (rs == null) {
                return null;
            }

            DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
            addResultSetTrace(poolableResultSet);

            return poolableResultSet;
        } catch (Throwable t) {
            errorCheck(t);

            throw checkException(t);
        } finally {
	        //调用连接对象的afterExecute()方法
            conn.afterExecute();
        }
        ···省略部分代码···
    }

此时,推测有关running状态跟踪是通过beforeExecute()afterExecute()进行跟踪。

    final void beforeExecute() {
        //得到当前连接的持有者
        final DruidConnectionHolder holder = this.holder;
        //持有者非空并且开启了removeAbandoned功能
        if (holder != null && holder.dataSource.removeAbandoned) {
            //设置当前连接正在执行中
            running = true;
        }
    }

    final void afterExecute() {
        //得到当前连接的持有者
        final DruidConnectionHolder holder = this.holder;
        //持有者非空
        if (holder != null) {
            //获得连接池对象
            DruidAbstractDataSource dataSource = holder.dataSource;
            //判断是否开启removeAbandoned
            if (dataSource.removeAbandoned) {
                //设置当前连接不在执行中
                running = false;
                //设置最后活跃时间
                holder.lastActiveTimeMillis = System.currentTimeMillis();
            }
            //更新连接池onFatalError状态
            dataSource.onFatalError = false;
        }
    }

由此可知,开启removeAbandoned后,连接将在执行前后对running状态进行变更,以确定当前SQL是否正在执行中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值