Java EE アプリケーションで接続リークをチェックする方法はありますか?
アプリケーションはローカル マシンで実行されています。MySQL データベースを使用し、ユーザーは自分の詳細をこのデータベースに入力します。
私の意見では、接続漏れとは、接続オブジェクトを適切に閉じていないことを意味します。アプリケーションで作成するデータベース接続が多すぎます。データベース接続に接続漏れがないか確認したい。
Java EE アプリケーションで接続リークをチェックする方法はありますか?
アプリケーションはローカル マシンで実行されています。MySQL データベースを使用し、ユーザーは自分の詳細をこのデータベースに入力します。
私の意見では、接続漏れとは、接続オブジェクトを適切に閉じていないことを意味します。アプリケーションで作成するデータベース接続が多すぎます。データベース接続に接続漏れがないか確認したい。
log4jdbcは、他の JDBC ドライバーの SQL および/または JDBC 呼び出しをログに記録できる Java JDBC ドライバーで、接続の開閉イベントをログに記録し、開いているすべての接続番号をダンプするロガーを備えています。これは、接続リークの問題を突き止めるのに非常に役立ちます。
確認する必要があるもう 1 つのツールはConnLeakFinderです。これは、Java コード内の jdbc 接続リークを特定するための簡単なツールです。私はそれについての経験はありません。
Java EE アプリ サーバーを使用している場合は、接続が終了したときに接続を確認し、戻ってこないときに古い接続を再利用するように構成できるはずです。
接続漏れは確かに問題です。コード内の非常に多くの場所に接続管理が散らばっていて、それらすべてを見つけるのが大きな問題になっているとしたら、私は心配です。明確に定義された永続層内でのみ使用される Java EE 接続プールが見られることを期待しています。接続は、その作業単位のトランザクションを管理するサービス レイヤーによって開かれ、ユース ケースが終了するとすぐに、finally ブロックのメソッド スコープ内で閉じられる必要があります。
そうでない場合は、リファクタリングの時期だと思います。
次に、接続ファクトリを使用します。
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接続プール機能があります。
FindBugを使用してみてください。これは静的コード分析ツールであり、Eclipse プラグインおよびスタンドアロン アプリケーションとして利用できます。接続漏れとは別に、アプリケーションの他の問題も検出されます
接続リークに取り組む最善の方法は、テスト中に行うことです。
自動ユーティリティを使用して、各テストで接続リークがあるかどうかを確認できます。
@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 );
}
}
}
これで、テストを実行すると、接続がリークしているときにエラーが発生します。