2010-06-19 55 views
5

¿Hay alguna manera de verificar la fuga de conexión en una aplicación Java EE?¿Cómo comprobar la fuga de conexión de la base de datos en la aplicación Java EE?

La aplicación se está ejecutando en mi máquina local. Utiliza una base de datos MySQL y un usuario ingresa sus detalles en esta base de datos.

En mi opinión, fuga de conexión significa no cerrar correctamente el objeto de conexión. Estoy creando demasiadas conexiones de bases de datos en mi aplicación. Quiero comprobar si hay alguna fuga de conexión en las conexiones de la base de datos.

Respuesta

10

log4jdbc, un controlador Java JDBC que puede registrar llamadas SQL y/o JDBC para otros controladores JDBC, tiene un registrador que registra la apertura y cierre de eventos, así como el vaciado de todos los números de conexión abiertos. Esto es muy útil para detectar problemas de fuga de conexión.

Otra herramienta que es posible que desee comprobar es ConnLeakFinder, una herramienta sencilla para localizar fugas de conexión JDBC en código Java. Aunque no tengo ninguna experiencia con eso.

+0

¿Cómo se compara esto con p6spy? –

+1

@ Thorbjørn Lo considero una alternativa más moderna (la última versión de P6Spy fue hace más de 7 años): usa SLF4J, ofrece algunas características y opciones de configuración que me gustan, es compatible con JDBC 4 ... Lo estoy usando como reemplazo ahora. –

+0

ConnLeakFinder funcionó muy bien para mí. Gracias por el enlace. –

0

intente utilizar FindBug. Es una herramienta de análisis de código estático y está disponible como complemento de eclipse y como aplicación independiente. Además de Connection leackage encontrará otros problemas en su aplicación también

1

Si está utilizando un servidor de aplicaciones Java EE, debería poder configurarlo para verificar las conexiones cuando salen y reclamar conexiones obsoletas cuando no funcionan Vuelve.

La fuga de conexión es realmente un problema. Me preocuparía si tuviera una administración de conexiones dispersa en tantos lugares en el código que fuera un gran problema encontrarlos a todos. Esperaría ver un grupo de conexiones de Java EE que se usara solo dentro de una capa de persistencia bien definida. Las conexiones deben ser abiertas por una capa de servicio que gestiona la transacción para esa unidad de trabajo y la cierra tan pronto como el caso de uso finaliza, dentro del alcance del método en un bloque finally.

Si eso no es cierto, creo que es hora de refactorizar.

0

Utilice una fábrica de conexiones, por ejemplo:

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.SQLException; 

public class ConnectionFactory { 
    private static Connection connection; 

    public static synchronized Connection getConnection() throws SQLException { 
     if (connection == null || connection.isClosed()) { 
      connection = DriverManager.getConnection("url"); 
     } 
     return connection; 
    } 
} 

De esta manera nunca dejar desatendidas las conexiones atrás. Use un grupo de conexiones si necesita más de una conexión (para rendimiento). La mayoría de los servidores de aplicaciones tienen una instalación de grupo de conexiones JDBC.

0

La mejor manera de tackle connection leaks is to do it during testing.

Puede utilizar una utilidad automatizada para que cada prueba verifique si hay una fuga de conexión.

@BeforeClass 
public static void initConnectionLeakUtility() { 
    if (enableConnectionLeakDetection) { 
     connectionLeakUtil = new ConnectionLeakUtil(); 
    } 
} 

@AfterClass 
public static void assertNoLeaks() { 
    if (enableConnectionLeakDetection) { 
     connectionLeakUtil.assertNoLeaks(); 
    } 
} 

El ConnectionLeakUtil se parece a esto:

public class ConnectionLeakUtil { 

    private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE; 

    private List idleConnectionCounters = 
     Arrays.asList(
      H2IdleConnectionCounter.INSTANCE, 
      OracleIdleConnectionCounter.INSTANCE, 
      PostgreSQLIdleConnectionCounter.INSTANCE, 
      MySQLIdleConnectionCounter.INSTANCE 
    ); 

    private IdleConnectionCounter connectionCounter; 

    private int connectionLeakCount; 

    public ConnectionLeakUtil() { 
     for (IdleConnectionCounter connectionCounter : 
      idleConnectionCounters) { 
      if (connectionCounter.appliesTo( 
       Dialect.getDialect().getClass())) { 
       this.connectionCounter = connectionCounter; 
       break; 
      } 
     } 
     if (connectionCounter != null) { 
      connectionLeakCount = countConnectionLeaks(); 
     } 
    } 

    public void assertNoLeaks() { 
     if (connectionCounter != null) { 
      int currentConnectionLeakCount = countConnectionLeaks(); 
      int diff = currentConnectionLeakCount - connectionLeakCount; 
      if (diff > 0) { 
       throw new ConnectionLeakException( 
        String.format(
         "%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d", 
         diff, 
         connectionLeakCount, 
         currentConnectionLeakCount 
        ) 
       ); 
      } 
     } 
    } 

    private int countConnectionLeaks() { 
     try (Connection connection = newConnection()) { 
      return connectionCounter.count(connection); 
     } 
     catch (SQLException e) { 
      throw new IllegalStateException(e); 
     } 
    } 

    private Connection newConnection() { 
     try { 
      return DriverManager.getConnection(
       jdbcProperties.getUrl(), 
       jdbcProperties.getUser(), 
       jdbcProperties.getPassword() 
      ); 
     } 
     catch (SQLException e) { 
      throw new IllegalStateException(e); 
     } 
    } 
} 

Los IdleConnectionCounter implementaciones se pueden encontrar en este blog post, y la versión de MySQL así:

public class MySQLIdleConnectionCounter implements IdleConnectionCounter { 

    public static final IdleConnectionCounter INSTANCE = 
     new MySQLIdleConnectionCounter(); 

    @Override 
    public boolean appliesTo(Class<? extends Dialect> dialect) { 
     return MySQL5Dialect.class.isAssignableFrom(dialect); 
    } 

    @Override 
    public int count(Connection connection) { 
     try (Statement statement = connection.createStatement()) { 
      try (ResultSet resultSet = statement.executeQuery(
        "SHOW PROCESSLIST")) { 
       int count = 0; 
       while (resultSet.next()) { 
        String state = resultSet.getString("command"); 
        if ("sleep".equalsIgnoreCase(state)) { 
         count++; 
        } 
       } 
       return count; 
      } 
     } 
     catch (SQLException e) { 
      throw new IllegalStateException(e); 
     } 
    } 
} 

Ahora, al ejecutar su pruebas, obtendrá una falla cuando se está filtrando una conexión.

Cuestiones relacionadas