连接池 Druid (补充) - removeAbandoned

removeAbandoned参数指定连接池是否移除被遗弃的连接。

具体的逻辑分两部分:一部分是设置活动连接、准备将来判断是否应该被遗弃,这个动作是在getConnection获取连接后执行的;另外一部分是在连接回收任务DestroyTask中,我们在分析连接回收的文章中忽略了这部分内容,今天补充一下。

getConnection方法获取连接之后判断removeAbandoned参数打开的话,就设置当前连接的connectedTimeNano为系统时间,然后将当前连接放入activeConnections中,准备被回收线程检查、移除。

代码在getConnectionDirect中:
```
           if (removeAbandoned) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.connectStackTrace = stackTrace;
                poolableConnection.setConnectedTimeNano();
                poolableConnection.traceEnable = true;

                activeConnectionLock.lock();
                try {
                    activeConnections.put(poolableConnection, PRESENT);
                } finally {
                    activeConnectionLock.unlock();
                }
            }
```


第二部分需要回忆一下上一篇文章中的DestroyTask的run方法,在调用shrink之后判断RemoveAbandoned则调用removeAbandoned()方法:
```

        @Override
        public void run() {
            shrink(true, keepAlive);

            if (isRemoveAbandoned()) {
                removeAbandoned();
            }
        }
```
removeAbandoned方法源码,分段贴出:
```
   public int removeAbandoned() {
        int removeCount = 0;

        long currrentNanos = System.nanoTime();

        List abandonedList = new ArrayList();

        activeConnectionLock.lock();
        try {
            Iterator iter = activeConnections.keySet().iterator();

            for (; iter.hasNext();) {
                DruidPooledConnection pooledConnection = iter.next();

                if (pooledConnection.isRunning()) {
                    continue;
                }

                long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);

                if (timeMillis >= removeAbandonedTimeoutMillis) {
                    iter.remove();
                    pooledConnection.setTraceEnable(false);
                    abandonedList.add(pooledConnection);
                }
            }
        } finally {
            activeConnectionLock.unlock();
        }
```
首先activeConnectionLock加锁。

循环检查activeConnections中的每一个连接。

如果连接isRunning()的话,则不处理当前连接。连接的running状态是在连接执行sql语句前调用beforeExecute方法设置为true、执行完成后调用afterExecute方法设置为false的。也就是说running状态其实就是字面意思,如果一个连接正在执行sql语句,running为true,执行完成后,running为false。

计算timeMillis为连接获取之后到现在的时长,注意connectedTimeNano是在我们上面所说的第一步:连接获取之后设置的。

如果timeMillis大于参数设定的移除时长removeAbandonedTimeoutMillis的话,则连接放入abandonedList中准备移除。

之后,释放activeConnectionLock锁。

后面的代码就不贴出了,很简单,就是从abandonedList列表中逐个取出连接,关闭连接,标记当前连接的abandoned=true。

#### 总结
结合Druid连接池的连接归还过程,总结一下removeAbandoned参数的总体使用逻辑:

1. removeAbandoned参数设置为true的话,需要配合设置一个removeAbandonedTimeoutMillis,认定连接被遗弃的时长
2. 从连接给获取到开始计时,并将连接放入到活动连接activeConnections中
3. 连接在执行sql语句过程中(running状态)不会被认定为遗弃而回收
4. 连接执行完成、归还到连接池的时候,如果还没有被认定为遗弃,则从activeConnections中移除(安全了...)
5. 连接尚未归还给连接池、也不在running状态的情况下(比如应用在执行完sql语句之后不close连接的情况下),连接回收线程检查该连接自从被getConnection获取之后到现在的时长如果大于removeAbandonedTimeoutMillis的话,就会给该连接打标记:需要被遗弃。并关闭该连接,不再允许其归还到连接池


有关removeAbandoned参数,在初始化过程中的initCheck()方法中有一段代码:
```
         if (removeAbandoned) {
            LOG.warn("removeAbandoned is true, not use in production.");
        }
```
意思很明确,removeAbandoned参数并不是用在生产环境中的,是专为测试环境准备的参数。***因此,建议大家开发过程中干脆忘记这个参数,不要设置为打开。***

上一篇 [连接池 Druid (四) - 连接归还](https://segmentfault.com/a/1190000043740158)
下一篇 [Mybatis的log机制](https://segmentfault.com/a/1190000043769784)

你可能感兴趣的:(jvm,java,数据结构)