[英]How to check the Database Connection leakage in Java EE application?
有沒有辦法檢查Java EE應用程序中的連接泄漏?
應用程序在我的本地計算機上運行。 它使用MySQL數據庫,用戶將其詳細信息輸入此數據庫。
在我看來,連接泄漏意味着沒有正確關閉連接對象。 我在我的應用程序中創建了太多的數據庫連接。 我想檢查數據庫連接中是否存在任何連接泄漏。
log4jdbc是一個可以記錄其他JDBC驅動程序的SQL和/或JDBC調用的Java JDBC驅動程序,它有一個記錄器,用於記錄連接打開和關閉事件以及轉儲所有打開的連接號。 這對於追蹤連接泄漏問題非常有用 。
您可能想要檢查的另一個工具是ConnLeakFinder , 這是一個在java代碼中查明jdbc連接泄漏的簡單工具 。 雖然我沒有任何經驗。
如果您正在使用Java EE應用服務器,則應該能夠將其配置為在外出時檢查連接,並在它們不返回時回收過時的連接。
連接泄漏確實是一個問題。 如果您在代碼中的許多地方分散了連接管理,我會擔心找到它們是一個很大的問題。 我希望看到一個僅在定義良好的持久層中使用的Java EE連接池。 連接應該由管理該工作單元的事務的服務層打開,並在用例結束后立即關閉,在finally塊的方法范圍內。
如果那不是真的,我認為現在是重構的時候了。
嘗試使用FindBug 。 它是一個靜態代碼分析工具,可用作eclipse插件以及獨立應用程序。 除了連接泄漏之外,它還會在您的應用程序中發現其他問題
使用連接工廠,例如:
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連接池功能。
解決連接泄漏的最佳方法是在測試期間執行此操作 。
您可以使用自動化實用程序,以便每個測試驗證是否存在連接泄漏。
@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
實現可以在這篇博文中找到,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 );
}
}
}
現在,當您運行測試時,在連接泄露時會出現故障。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.