Lectura del código fuente de Druid Proceso de reciclaje de 7-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.

Después de usar la conexión en Druid, debe reciclarse, y el método para reciclar la conexión es el método de reciclaje. El objetivo principal del reciclaje es borrar/restablecer el estado de la conexión, colocarlo al final de la matriz de conexiones del grupo de conexiones y luego enviar el mensaje de notificación de la variable de condición notEmpty del bloqueo del grupo de conexiones, de modo que el el subproceso del consumidor en espera puede obtener la conexión.

1. Proceso de reciclaje

Lo primero que debe hacer en el método de reciclaje es determinar si el hilo de reciclaje es el mismo hilo. Si no, imprima la salida del registro.

if (logDifferentThread //
    && (!isAsyncCloseConnectionEnable()) //
    && pooledConnection.ownerThread != Thread.currentThread()//
) {
    LOG.warn("get/close not same thread");
}

复制代码

Este nivel de registro es una advertencia, que es a lo que debemos prestar especial atención cuando usamos el grupo de conexiones. En términos generales, solo el grupo de conexiones tiene una fuga de conexión. El hilo que usa la conexión mantiene la conexión durante mucho tiempo sin realizar operaciones específicas. y la conexión se pierde Solo el hilo monitoreado cerrará la conexión. Esta situación requiere una atención especial. Además, el mecanismo removeAbandoned del mecanismo de monitoreo de fugas de conexión también llamará al método de reciclaje para reciclar.

Tanto pooledConnection.traceEnable como activeConnections son parámetros relacionados con el mecanismo removeAbandoned, que serán analizados en detalle en el seguimiento del mecanismo removeAbandoned.

if (pooledConnection.traceEnable) {
    Object oldInfo = null;
    activeConnectionLock.lock();
    try {
        if (pooledConnection.traceEnable) {
            //将连接从activeConnections中移除 考虑到多线程场景,要加锁
            oldInfo = activeConnections.remove(pooledConnection);
            pooledConnection.traceEnable = false;
        }
    } finally {
        activeConnectionLock.unlock();
    }
    if (oldInfo == null) {
        if (LOG.isWarnEnabled()) {
            LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
        }
    }
}
复制代码

Procesamiento de reversión: si la conexión no se confirma automáticamente y no es de solo lectura, la operación de reversión se realiza primero al reciclar.

// check need to rollback?
if ((!isAutoCommit) && (!isReadOnly)) {
    pooledConnection.rollback();
}
复制代码

Procesamiento de reinicio: es necesario juzgar si se trata de un escenario de subprocesos múltiples aquí. Si no es el mismo subproceso, porque el subproceso de la conexión en sí también puede llamar a reciclar cuando se libera después de que se completa la llamada, por lo que el enhebrado debe ser considerado en este lugar.

// reset holder, restore default settings, clear warnings
boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread();
if (!isSameThread) {
    final ReentrantLock lock = pooledConnection.lock;
    lock.lock();
    try {
        holder.reset();
    } finally {
        lock.unlock();
    }
} else {
    holder.reset();
}
复制代码

La parte más crítica es el método de reinicio:

//清空Listeners
connectionEventListeners.clear();
statementEventListeners.clear();

lock.lock();
try {
    for (Object item : statementTrace.toArray()) {
        Statement stmt = (Statement) item;
        //关闭statement
        JdbcUtils.close(stmt);
    }
    //清空statementTrace
    statementTrace.clear();
} finally {
    lock.unlock();
}
//清空warning信息
conn.clearWarnings();
复制代码

El método de reinicio cierra todas las declaraciones en la conexión y borra el contenido de las luces de alarma relacionadas.

Manejo de conexión cercana:

//如果状态为discard 则直接退出
if (holder.discard) {
    return;
}
//如果超过连接最大的使用次数,那么也将关闭连接
if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
    discardConnection(holder);
    return;
}
复制代码

Si el estado es udiscard en este lugar, salga directamente del método de reciclaje. será descartado en métodos posteriores. La conexión también se cierra si se alcanza el número máximo de llamadas.

Cerrar limpieza: Si se ha cerrado la conexión, bloquearla y restar el contador.

if (physicalConnection.isClosed()) {
        lock.lock();
        try {
            if (holder.active) {
                activeCount--;
                holder.active = false;
            }
            closeCount++;
        } finally {
            lock.unlock();
        }
        return;
    }
复制代码

Procesamiento testOnReturn: si testOnReturn está activado, envíe datos de prueba, si la prueba falla, cierre la conexión.

if (testOnReturn) {
    boolean validate = testConnectionInternal(holder, physicalConnection);
    if (!validate) {
        JdbcUtils.close(physicalConnection);

        destroyCountUpdater.incrementAndGet(this);

        lock.lock();
        try {
            if (holder.active) {
                activeCount--;
                holder.active = false;
            }
            closeCount++;
        } finally {
            lock.unlock();
        }
        return;
    }
}
复制代码

如果配置了druid.phyTimeoutMillis,那么回收的时候需要对连接进行超时检测:

if (phyTimeoutMillis > 0) {
    long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
    if (phyConnectTimeMillis > phyTimeoutMillis) {
        discardConnection(holder);
        return;
    }
}
复制代码

最关键的代码:

lock.lock();
try {
    //修改active状态和activeCount计数器
    if (holder.active) {
        activeCount--;
        holder.active = false;
    }
    //增加closeCount计数器
    closeCount++;
    //将连接放置到数组的末尾
    result = putLast(holder, currentTimeMillis);
    recycleCount++;
} finally {
    lock.unlock();
}
复制代码

这是连接回收的核心代码,就是将连接放置到数组的末尾。 在putLast方法中会调用 notEmpty.signal();这样消费者线程就会产生调用。

2.recycle的调用时机

recycle方法将在连接的close方法中被调用。在需要关闭连接的时候,调用recycle,将符合规则的连接添加到连接池的末尾。 调用recycle的方法:

  public void close() throws SQLException ;
  
  public void syncClose() throws SQLException ;
复制代码

close和syncClose方法都会调用recycle。 在关闭连接之前,跟获取连接一样,都要进行责任链模式的filter处理。

if (filters.size() > 0) {
    FilterChainImpl filterChain = new FilterChainImpl(dataSource);
    filterChain.dataSource_recycle(this);
} else {
    recycle();
}
复制代码

这个过程与getConnection方法执行filter的过程类似。

Supongo que te gusta

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