【问题标题】:How to check the Database Connection leakage in Java EE application?如何检查 Java EE 应用程序中的数据库连接泄漏?
【发布时间】:2010-06-19 12:39:53
【问题描述】:

有没有办法检查 Java EE 应用程序中的连接泄漏?

应用程序正在我的本地计算机上运行。它使用 MySQL 数据库,用户将他的详细信息输入到该数据库中。

在我看来,连接泄漏意味着没有正确关闭连接对象。 我在我的应用程序中创建了太多的数据库连接。我想检查一下数据库连接是否有连接泄漏。

【问题讨论】:

    标签: java mysql jdbc connection-leaks


    【解决方案1】:

    log4jdbc,一个 Java JDBC 驱动程序,可以记录 SQL 和/或其他 JDBC 驱动程序的 JDBC 调用,有一个记录器,记录连接打开和关闭事件以及转储所有打开的连接号。这对于查找连接泄漏问题非常有用

    您可能想要检查的另一个工具是ConnLeakFinder一个用于查明 java 代码中 jdbc 连接泄漏的简单工具。不过我没有任何经验。

    【讨论】:

    • 这与 p6spy 相比如何?
    • @Thorbjørn 我认为它是一种更现代的替代品(P6Spy 的最新版本是 7 多年前):它使用 SLF4J,它提供了一些我喜欢的特性和配置选项,它支持 JDBC 4。 ..我现在用它作为替代品。
    • ... ConnLeakFinder 自 2009 年以来一直没有更新 :) 想知道为什么这些工具会发生这种情况。
    【解决方案2】:

    如果您使用的是 Java EE 应用服务器,您应该能够将其配置为在连接断开时检查连接,并在旧连接不回来时回收它们。

    连接泄漏确实是个问题。如果您将连接管理分散在代码中的很多地方,我会担心,要找到它们是个大问题。我希望看到一个仅在定义明确的持久层中使用的 Java EE 连接池。连接应该由管理该工作单元的事务的服务层打开,并在用例结束后立即关闭它,在 finally 块的方法范围内。

    如果不是这样,我认为是时候重构了。

    【讨论】:

      【解决方案3】:

      尝试使用FindBug。它是一个静态代码分析工具,可作为 eclipse 插件和独立应用程序使用。除了连接泄漏,它还会在您的应用程序中发现其他问题

      【讨论】:

        【解决方案4】:

        使用连接工厂,例如:

        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;
            }
        }
        

        这样您就永远不会留下无人看管的连接。如果您需要多个连接(出于性能考虑),请使用连接池。大多数应用服务器都有 JDBC 连接池设施。

        【讨论】:

          【解决方案5】:

          tackle connection leaks is to do it during testing 的最佳方式。

          您可以使用自动化实用程序,以便每次测试都验证是否存在连接泄漏。

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

          ConnectionLeakUtil 看起来像这样:

          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 );
                  }
              }
          }
          

          IdleConnectionCounter 的实现可以在这个blog post 中找到,MySQL 版本如下:

          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 );
                  }
              }
          }
          

          现在,当您运行测试时,如果连接泄漏,您会遇到故障。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-27
            相关资源
            最近更新 更多