Lectura de código fuente de Druid Proceso de reducción de 5-DruidDataSource

Este artículo ha participado en la actividad "Ceremonia de creación de recién llegados", y hemos iniciado juntos el camino de la creación de Nuggets.

El método de reducción es el método de ejecución específico para reciclar conexiones en el subproceso DestroyTask.

Primero adquiere el candado:

try {
    lock.lockInterruptibly();
} catch (InterruptedException e) {
    return;
}
复制代码

Después de eso, es necesario juzgar si el estado de inicialización se completó.Si se usa la inicialización asincrónica, es posible que se haya iniciado el subproceso DestoryTask, pero el conjunto de conexiones no se ha inicializado.

if (!inited) {
    return;
}
复制代码

Después de eso, se recorren las conexiones en el grupo de conexiones.En las conexiones, el número de conexiones que se pueden conectar se registra en la variable poolingCount. En este momento, se debe registrar un checkCount Esta variable es checkCount = poolingCount - minIdle, es decir, checkCount es el número de conexiones en el grupo de conexiones menos el número mínimo de conexiones inactivas para establecer minIdle.

Luego ingrese la lógica checkTime, checkTime es el parámetro que se pasa cuando se llama a Shrink, generalmente este parámetro es verdadero cuando se llama a DestroyTask. Después de eso, los parámetros de verificación son:

  • Determine si se agotó el tiempo de espera de la conexión física: phyConnectTimeMillis > phyTimeoutMillis. Si se agota el tiempo de espera, marque la conexión actual a la matriz evictConnections y salga del ciclo actual.
  • Determine si el tiempo de inactividad ha expirado:

Si el tiempo de inactividad es menor que el tiempo minEvictableIdleTimeMillis configurado y menor que el tiempo configurado de keepAliveBetweenTimeMillis(idleMillis < minEvictableIdleTimeMillis && idleMillis < keepAliveBetweenTimeMillis), el ciclo finaliza. Por el contrario, cuando idleMillis es mayor que minEvictableIdleTimeMillis o mayor que maxEvictableIdleTimeMillis, se marca en la matriz evictConnections.

  • 判断keepAlive是否超时:如果idleMillis >= keepAliveBetweenTimeMillis,则标记到keepAliveConnections数组。

如果checkTime为false,则将小于checkCount的全部连接都标记到evictConnections数组。

if (i < checkCount) {
    evictConnections[evictCount++] = connection;
} else {
    break;
}
复制代码

这之后进行removeCount的处理,removeCount = evictCount + keepAliveCount; 处理逻辑如下:

if (removeCount > 0) {
    //将connections从removeCount到poolingCount的连接向前移动poolingCount - removeCount。
    System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
    //将poolingCount - removeCount后续部分都置为空。
    Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
    poolingCount -= removeCount;
}
复制代码

这个逻辑实质上是将connections中计算出来的前N项都移除。 之前一直不理解这个逻辑,实际上需要详细看一下for循环中的逻辑。for循环中,如果checkTime为false,则直接将前面checkCount个连接都移除。 反之,由于connections中,通过recycle方法,将放回的连接都放在connections数组的最后面。get的连接也是从connections的尾部获取,那么可以确保connections的连接,index小的连接最少被使用。 那么在这里确定了需要移除的连接数之后,直接就可以将connetions的前面checkCount个连接都移除。

移除之后,可以解锁。之后对移除的连接进行处理。

} finally {
    lock.unlock();
}
复制代码

对于evict的连接:

if (evictCount > 0) {
    for (int i = 0; i < evictCount; ++i) {
        DruidConnectionHolder item = evictConnections[i];
        Connection connection = item.getConnection();
        //关闭连接
        JdbcUtils.close(connection);
        //更新计数器
        destroyCountUpdater.incrementAndGet(this);
    }
    //将evictConnections清空
    Arrays.fill(evictConnections, null);
}
复制代码

关闭连接并清空evictConnections。

对于keepAliveCount连接,则需要分几种情况进行讨论:

if (keepAliveCount > 0) {
    // keep order
    for (int i = keepAliveCount - 1; i >= 0; --i) {
        DruidConnectionHolder holer = keepAliveConnections[i];
        Connection connection = holer.getConnection();
        holer.incrementKeepAliveCheckCount();

        boolean validate = false;
        //校验连接是否还可用
        try {
            this.validateConnection(connection);
            validate = true;
        } catch (Throwable error) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("keepAliveErr", error);
            }
            // skip
        }

        boolean discard = !validate;
        //如果可用,则直接put到connections中,放置到尾部。
        if (validate) {
            holer.lastKeepTimeMillis = System.currentTimeMillis();
            boolean putOk = put(holer, 0L, true);
            if (!putOk) {
                discard = true;
            }
        }
		
		//如果不可用,则关闭连接
        if (discard) {
            try {
                connection.close();
            } catch (Exception e) {
                // skip
            }

            lock.lock();
            //加锁更新计数器
            try {
                discardCount++;

                if (activeCount + poolingCount <= minIdle) {
                    emptySignal();
                }
            } finally {
                lock.unlock();
            }
        }
    }
    this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
    Arrays.fill(keepAliveConnections, null);
}
复制代码

对于keepalive状态的连接,为了更好的复用该连接,则首先判断该连接是否可用,如果可用,则调用put方法,将该连接的状态更新之后,放置到连接池的尾部。 可见,shrink中,并非所有的连接都会关闭,对于keepalive状态的连接,需要判断是否可用。可用的连接还可再次复用。

此时还有一种情况需要考虑,就是此时可用的连接仍然不够minIdle,那么连接池不满,需要继续创建连接。这个状态为needFill:

//keepAlive状态,且连接池中的连接加上被使用的连接仍然小于minIdle
if (keepAlive && poolingCount + activeCount < minIdle) {
    needFill = true;
}
复制代码

处理逻辑:

if (needFill) {
//加锁
    lock.lock();
    try {
        //如果minIdle 减去activeCount + poolingCount + createTaskCount 仍然不满,则通知创建线程创建连接
        int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
        for (int i = 0; i < fillCount; ++i) {
            emptySignal();
        }
        //解锁
    } finally {
        lock.unlock();
    }
} else if (onFatalError || fatalErrorIncrement > 0) {
    lock.lock();
    try {
        emptySignal();
    } finally {
        lock.unlock();
    }
}
复制代码

needFill和onFatalError 都需要通知生产者继续创建连接。

至此,shrink方法分析完毕。

Supongo que te gusta

Origin juejin.im/post/7078495292407939080
Recomendado
Clasificación