2010-06-19 22 views
5

Có cách nào để kiểm tra rò rỉ kết nối trong ứng dụng Java EE không?Làm thế nào để kiểm tra rò rỉ kết nối cơ sở dữ liệu trong ứng dụng Java EE?

Ứng dụng đang chạy trên máy cục bộ của tôi. Nó sử dụng cơ sở dữ liệu MySQL và người dùng nhập các chi tiết của mình vào cơ sở dữ liệu này.

Trong ý kiến ​​rò rỉ kết nối của tôi có nghĩa là không đóng đối tượng kết nối đúng cách. Tôi đang tạo quá nhiều kết nối cơ sở dữ liệu trong ứng dụng của mình. Tôi muốn kiểm tra xem có rò rỉ kết nối nào trong các kết nối cơ sở dữ liệu không.

Trả lời

10

log4jdbc, một trình điều khiển JDBC Java có thể ghi nhật ký các cuộc gọi SQL và/hoặc JDBC cho các trình điều khiển JDBC khác, có một nhật ký mà ghi lại kết nối mở và đóng sự kiện cũng như bán tất cả các kết nối mở. Điều này rất hữu ích cho việc tìm kiếm các vấn đề rò rỉ kết nối.

Một công cụ khác mà bạn có thể muốn kiểm tra là ConnLeakFinder, một công cụ đơn giản để xác định rò rỉ kết nối jdbc trong mã java. Tôi không có bất kỳ kinh nghiệm với nó mặc dù.

+0

Điều này so sánh với p6spy như thế nào? –

+1

@ Thorbjørn Tôi coi nó như là một sự thay thế hiện đại hơn (bản phát hành mới nhất của P6Spy là 7+ năm trước): nó sử dụng SLF4J, nó cung cấp một số tính năng và tùy chọn cấu hình mà tôi thích, nó hỗ trợ JDBC 4 ... thay thế ngay bây giờ. –

+0

ConnLeakFinder làm việc rất tốt cho tôi. Cảm ơn các liên kết. –

0

thử sử dụng FindBug. Nó là một công cụ phân tích mã tĩnh và có sẵn như là plugin eclipse cũng như ứng dụng độc lập. Ngoài việc kết nối, nó cũng sẽ tìm thấy các vấn đề khác trong ứng dụng của bạn

1

Nếu bạn đang sử dụng máy chủ ứng dụng Java EE, bạn có thể cấu hình nó để kiểm tra kết nối khi chúng ra ngoài và lấy lại kết nối cũ khi chúng không ' Tôi quay lại.

Rò rỉ kết nối thực sự là một vấn đề. Tôi sẽ lo lắng nếu bạn có quản lý kết nối nằm rải rác ở rất nhiều nơi trong mã rằng đó là một vấn đề lớn để tìm tất cả. Tôi hy vọng sẽ thấy một hồ bơi kết nối Java EE đã được sử dụng chỉ trong một lớp kiên trì được xác định rõ. Các kết nối sẽ được mở bởi một tầng dịch vụ quản lý giao dịch cho đơn vị công việc đó và đóng nó ngay khi trường hợp sử dụng kết thúc, trong phạm vi phương thức trong một khối cuối cùng.

Nếu điều đó không đúng, tôi nghĩ đã đến lúc để tái cấu trúc.

0

Sử dụng một nhà máy kết nối, ví dụ:

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

Bằng cách này bạn không bao giờ rời khỏi các kết nối không cần giám sát đằng sau. Sử dụng một hồ bơi kết nối nếu bạn cần nhiều kết nối (để thực hiện). Hầu hết các trình ứng dụng đều có cơ sở pool kết nối JDBC.

0

Cách tốt nhất để tackle connection leaks is to do it during testing.

Bạn có thể sử dụng tiện ích tự động để mỗi thử nghiệm xác minh xem có rò rỉ kết nối hay không.

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

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

Các ConnectionLeakUtil trông như thế này:

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

Các IdleConnectionCounter triển khai có thể được tìm thấy trong blog post này, và phiên bản MySQL như thế này:

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

Bây giờ, khi bạn chạy của bạn kiểm tra, bạn sẽ gặp lỗi khi kết nối bị rò rỉ.

Các vấn đề liên quan