前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >tomcat jdbc数据库连接池详解之PoolCleaner

tomcat jdbc数据库连接池详解之PoolCleaner

作者头像
johnhuster的分享
发布2022-03-29 14:41:14
6250
发布2022-03-29 14:41:14
举报
文章被收录于专栏:johnhuster

PoolCleaner是一个定时任务,该任务在创建线程池时自动启动,该任务定期执行哪些工作呢,带着这个问题进入源码:

代码语言:javascript
复制
        @Override
        public void run() {
            ConnectionPool pool = this.pool.get();
            if (pool == null) {
                stopRunning();
            } else if (!pool.isClosed() &&
                    (System.currentTimeMillis() - lastRun) > sleepTime) {
                lastRun = System.currentTimeMillis();
                try {
                    if (pool.getPoolProperties().isRemoveAbandoned())
                        pool.checkAbandoned();
                    if (pool.getPoolProperties().getMinIdle() < pool.idle
                            .size())
                        pool.checkIdle();
                    if (pool.getPoolProperties().isTestWhileIdle())
                        pool.testAllIdle();
                } catch (Exception x) {
                    log.error("", x);
                }
            }
        }

而checkAbandoned、checkIdle、testAllIdle的执行是受条件约束的,比如checkAbandoned必须在连接池属性removeAbandoned配置为true时才会执行,更多tomcat jdbc连接池配置请参考官网:http://tomcat.apache.org/tomcat-8.5-doc/jdbc-pool.html

下面将一一介绍checkAbandoned、checkIdle、testAllIdle三个任务到底做了些什么工作,

代码语言:javascript
复制
  /**

checkAbandoned会清除一些使用时间过长的数据库连接,

要注意的是这部分工作针对的就是使用中的数据库连接,及ConnectionPool类BlockingQueue<PooledConnection> busy属性!!

**/

  public void checkAbandoned() {
        try {
            if (busy.size()==0) return;
            Iterator<PooledConnection> locked = busy.iterator();
            int sto = getPoolProperties().getSuspectTimeout();
            while (locked.hasNext()) {
                PooledConnection con = locked.next();
                boolean setToNull = false;
                try {

                    //操作之前加锁
                    con.lock();
                    //该连接已被移至idle队列中
                    if (idle.contains(con))
                        continue;
                    long time = con.getTimestamp();
                    long now = System.currentTimeMillis();
                    if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {
                       //从busy队列中移除该连接
                       busy.remove(con);

                        //废弃该连接
                        abandon(con);
                        setToNull = true;
                    } else if (sto > 0 && (now - time) > (sto*1000)) {
                        //将该连接列为疑似要被废弃状态并发送废弃消息
                        suspect(con);
                    } else {
                        //do nothing
                    } //end if
                } finally {
                    con.unlock();
                    if (setToNull)
                        con = null;
                }
            } //while
        } catch (ConcurrentModificationException e) {
            log.debug("checkAbandoned failed." ,e);
        } catch (Exception e) {
            log.warn("checkAbandoned failed, it will be retried.",e);
        }
    }

    protected boolean shouldAbandon() {
        if (poolProperties.getAbandonWhenPercentageFull()==0) return true;
        float used = busy.size();
        float max  = poolProperties.getMaxActive();
        float perc = poolProperties.getAbandonWhenPercentageFull();
        return (used/max*100f)>=perc;
    }

shouldAbandon方法busy状态的连接是否可以被废弃,被废弃需要满足以下条件:

busy态连接数/最大允许存活量>最大允许的存活比例

代码语言:javascript
复制
    //该任务针对的是idle队列的连接,即ConnectionPool的BlockingQueue<PooledConnection> idle属性
 public void checkIdle(boolean ignoreMinSize) {

        //ignoreMinSize传入值为false
        try {
            if (idle.size()==0) return;
            long now = System.currentTimeMillis();
            Iterator<PooledConnection> unlocked = idle.iterator();
            //当前idle队列数目大于连接池设置的minIdle值,需要释放多余的空间连接
            while ( (ignoreMinSize || (idle.size()>=getPoolProperties().getMinIdle())) && unlocked.hasNext()) {
                PooledConnection con = unlocked.next();
                boolean setToNull = false;
                try {
                    con.lock();
                    //the con been taken out, we can't clean it up
                    //idle跟busy队列中数据可以互相转换,到底怎么转换的后期文章将会介绍
                    if (busy.contains(con))
                        continue;
                    long time = con.getTimestamp();
                    //具体看下面的shouldReleaseIdle方法
                    if (shouldReleaseIdle(now, con, time)) {
                        release(con);
                        idle.remove(con);
                        setToNull = true;
                    } else {
                        //do nothing
                    } //end if
                } finally {
                    con.unlock();
                    if (setToNull)
                        con = null;
                }
            } //while
        } catch (ConcurrentModificationException e) {
            log.debug("checkIdle failed." ,e);
        } catch (Exception e) {
            log.warn("checkIdle failed, it will be retried.",e);
        }

    }


    protected boolean shouldReleaseIdle(long now, PooledConnection con, long time) {
        if (con.getConnectionVersion() < getPoolVersion()) return true;
        //getReleaseTime返回的是连接池的minEvictableIdleTimeMillis配置,这个参数决定了一个连接在被废弃前所允许的最大idle时间
        else return (con.getReleaseTime()>0) && ((now - time) > con.getReleaseTime()) && (getSize()>getPoolProperties().getMinIdle());
    }
代码语言:javascript
复制
   //在空闲期检测所有的idle队列中连接
   public void testAllIdle() {
        try {
            if (idle.size()==0) return;
            Iterator<PooledConnection> unlocked = idle.iterator();
            while (unlocked.hasNext()) {
                PooledConnection con = unlocked.next();
                try {
                    con.lock();
                    //the con been taken out, we can't clean it up
                    if (busy.contains(con))
                        continue;
                    //如果该连接检验失败则可以从idle队列移除,并释放该数据库连接,由于使用数据库连接池的连接是从idle队列中获取的,为了保证连接的有效性需要定期检测这些连接,有些连接可能会被数据库服务端关闭,如果不校验就使用,很可能会使用到无效的连接!
                    if (!con.validate(PooledConnection.VALIDATE_IDLE)) {
                        idle.remove(con);
                        release(con);
                    }
                } finally {
                    con.unlock();
                }
            } //while
        } catch (ConcurrentModificationException e) {
            log.debug("testAllIdle failed." ,e);
        } catch (Exception e) {
            log.warn("testAllIdle failed, it will be retried.",e);
        }

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库专家服务
数据库专家服务(Database Expert Service,DBexpert)为您提供专业化的数据库服务。仅需提交您的具体问题和需求,即可获得腾讯云数据库专家的专业支持,助您解决各类专业化问题。腾讯云数据库专家服务团队均有10年以上的 DBA 经验,拥有亿级用户产品的数据库管理经验,以及丰富的服务经验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档