2022-05-19 Druid源码阅读——removeAbandoned参数(二)

1.removeAbandoned如何排查连接泄露

1.1 什么是连接泄露

连接池中连接数同一时间被用光(长SQL、连接执行完成后没有及时归还等均有可能造成),超过获取连接最大等待时间后仍然没有获取到连接。

1.2 如何通过removeAbandoned排查连接泄露

通过开启removeAbandoned参数,使其在每次获取连接时记录当前线程的堆栈信息。

public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
  		···省略部分代码···
        //判断是否开启removeAbandoned
        if (removeAbandoned) {
            //记录当前线程的堆栈信息(此操作很耗时)
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            poolableConnection.connectStackTrace = stackTrace;
            //记录连接开始时间
            poolableConnection.setConnectedTimeNano();
            //设置开启跟踪,设置后将在归还连接时清除当前连接的活跃状态
            poolableConnection.traceEnable = true;
            //获得锁,向activeConnections中置入连接对象
            activeConnectionLock.lock();
            try {
                activeConnections.put(poolableConnection, PRESENT);
            } finally {
                activeConnectionLock.unlock();
            }
        }
···省略部分代码···
        return poolableConnection;
    }
}

开启logAbandoned参数,使其能够在回收时将当前堆栈信息进行打印。

                //如果开启了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());
                }

通过开启removeAbandonedlogAbandoned参数,可以在异常日志中观测到被自动回收的连接(超过removeAbandonedTimeout)的,可以通过removeAbandonedTimeout参数调整并对异常日志进行观测,得到长时间未进行资源释放的连接。

2.为什么removeAbandoned不建议在线上开启

在开启removeAbandoned参数后,initCheck()方法将输出removeAbandoned is true, not use in production.日志,由此可见该参数并不建议在生产环境进行开启。

if (removeAbandoned) {
    LOG.warn("removeAbandoned is true, not use in production.");
}

该参数开启后存在以下问题:

  1. 每次获得连接时都会记录当前堆栈信息,造成不必要的空间消耗,并且该操作具有较长的时间消耗
  2. removeAbandonedTimeout不合理可能将正常的连接进行回收而不是归还
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值