3

最近、Java Web アプリケーションを作成してサーバーにデプロイしましたが、開発中やテスト中には発生しなかった異常な問題を発見しました。

ユーザーが久しぶりにログインし、データベースからのデータを表示しようとすると、表示するレコードがないことがページに示されます。ただし、ページを更新すると、ページ付けルールに従って最初のxレコードが表示されます。

ログを確認すると、次のことがわかります。

ERROR|19 09 2009|09 28 54|http-8080-4|myDataSharer.database_access.Database_Metadata_DBA| - Error getting types of columns of tabular Dataset 12

com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception: 

** BEGIN NESTED EXCEPTION ** 

java.io.EOFException

STACKTRACE:

java.io.EOFException
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1956)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2368)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2867)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1616)

数百行についても同様です。

このアプリケーションは現在、約 100 人のユーザーに設定されていますが、まだ完全には使用されていません。これは、Apache Tomcat サーブレット/jsps と MySQL データベースの間で接続プーリングを使用します。次のコード例は、データベース操作の一般的な配置を形成します。通常、ページごとにいくつかあります。

// Gets a Dataset.
public static Dataset getDataset(int DatasetNo) {
    ConnectionPool_DBA pool = ConnectionPool_DBA.getInstance();
    Connection connection = pool.getConnection();
    PreparedStatement ps = null;
    ResultSet rs = null;

    String query = ("SELECT * " +
                    "FROM Dataset " +
                    "WHERE DatasetNo = ?;");

    try {
        ps = connection.prepareStatement(query);
        ps.setInt(1, DatasetNo);
        rs = ps.executeQuery();
        if (rs.next()) {
            Dataset d = new Dataset();
            d.setDatasetNo(rs.getInt("DatasetNo"));
            d.setDatasetName(rs.getString("DatasetName"));
            ...

            }

            return d;
        }
        else {
            return null;
        }
    }
    catch(Exception ex) {
        logger.error("Error getting Dataset " + DatasetNo + "\n", ex);            
        return null;
    }
    finally {
        DatabaseUtils.closeResultSet(rs);
        DatabaseUtils.closePreparedStatement(ps);
        pool.freeConnection(connection);
    }
}

この問題を修正する方法をアドバイスできる人はいますか?

MySQL が最大 8 時間接続ポーリング接続を開いたままにしていることが原因だと思いますが、確実ではありません。

ありがとう

マーティン・オシェイ。


接続プーリングの方法について 1 つの点を明確にするために、アプリケーションで使用しているのは Oracle ではなく、次のような独自のクラスです。

package myDataSharer.database_access;

import java.sql.*;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import org.apache.log4j.Logger;

public class ConnectionPool_DBA {

    static Logger logger = Logger.getLogger(ConnectionPool_DBA.class.getName());

    private static ConnectionPool_DBA pool = null;
    private static DataSource dataSource = null;


    public synchronized static ConnectionPool_DBA getInstance() {
        if (pool == null) {
            pool = new ConnectionPool_DBA();
        }
        return pool;
    }

    private ConnectionPool_DBA() {
        try {
            InitialContext ic = new InitialContext();
            dataSource = (DataSource) ic.lookup("java:/comp/env/jdbc/myDataSharer");
        }
        catch(Exception ex) {
            logger.error("Error getting a connection pool's datasource\n", ex);
        }
    }

    public void freeConnection(Connection c) {
        try {
            c.close();
        }
        catch (Exception ex) {
            logger.error("Error terminating a connection pool connection\n", ex);           
        }
    }

    public Connection getConnection() {
        try {
            return dataSource.getConnection();
        }
        catch (Exception ex) {
            logger.error("Error getting a connection pool connection\n", ex);            
            return null;
        }
    }    
}

オラクルの言及は、私が似たような名前を使用しているためだと思います。

4

5 に答える 5

4

この状況を回避するためのヒントがいくつかあります。これらは、他の情報源、特に他のドライバーの接続プールの実装や他のアプリケーション サーバーから得られたものです。一部の情報は、JNDI データ ソースに関する Tomcat ドキュメントですでに利用可能です。

  1. プール内の接続が一定期間を超えて非アクティブな場合に閉じるクリーンアップ/リーパー スケジュールを確立します。データベースへの接続を 8 時間 (MySQL のデフォルト) 開いたままにしておくことはお勧めできません。ほとんどのアプリケーション サーバーでは、非アクティブな接続のタイムアウト値は構成可能で、通常は 15 分未満です (つまり、何度も再利用されない限り、接続をプールに 15 分以上残すことはできません)。Tomcat では、JNDI DataSource を使用する場合、removeAbandoned および removeAbandonedTimeout 設定を使用して同じことを行います。
  2. 新しい接続がプールからアプリケーションに返されるときは、最初にそれがテストされていることを確認してください。たとえば、私が知っているほとんどのアプリケーション サーバーは、「SELECT 1 FROM dual」を実行して Oracle データベースへの接続をテストするように構成できます。Tomcat では、validationQuery プロパティを使用して MySQL に適切なクエリを設定します。これは "SELECT 1" (引用符なし) だと思います。validationQuery プロパティの値を設定すると役立つ理由は、クエリの実行に失敗した場合、接続がプールから削除され、代わりに新しい接続が作成されるためです。

アプリケーションの動作に関する限り、ユーザーはおそらく、プールが古い接続をアプリケーションに返す結果を初めて目にします。2 回目は、プールはおそらく、アプリケーションのクエリを処理できる別の接続を返します。

Tomcat JNDI データ ソースは Commons DBCP に基づいているため、DBCP に適用可能な構成プロパティは Tomcat にも適用されます。

于 2009-09-19T17:27:34.013 に答える
3

Tomcat にプーリングを処理させ、JNDI を使用して単に接続を検索させるのではなく、コードで ConnectionPool_DBA を使用しているのはなぜだろうか。

MySQL で Oracle 接続プールを使用しているのはなぜですか? JNDI ルックアップと接続プーリングを行うときは、Apache DBCP ライブラリを好みます。私はそれが非常にうまく機能することを発見しました。

また、databaseUtils メソッドが例外をスローするかどうかも尋ねます。なぜなら、pool.freeConnection() を呼び出す前にいずれかの呼び出しが例外をスローした場合、その接続を解放することは決してないからです。

SQL 操作を実行するクラスには Connection インスタンスを渡す必要があり、Connection の取得と使用という二重の責任を負うべきではないため、あなたのコードはあまり好きではありません。永続化クラスは、それがより大きなトランザクションで使用されているかどうかを認識できません。接続を取得し、トランザクションを管理し、永続性クラスをマーシャリングし、完了時にクリーンアップする別のサービス レイヤーを用意することをお勧めします。

アップデート:

Google は、あなたと同じ名前の Oracle クラスを見つけました。より良い代替手段が簡単に利用できるときに独自のコードを作成したため、今ではあなたのコードが本当に好きではありません。私はあなたのものをすぐに捨てて、DBCP と JNDI を使用してこれをやり直します。

于 2009-09-19T17:24:28.653 に答える
2

このエラーは、サーバーが予期せず接続を閉じたことを示します。これは、次の2つの場合に発生する可能性があります。

  1. MySQLは、特定の時間(デフォルトは8時間)後にアイドル状態の接続を閉じます。これが発生すると、接続を閉じる責任を負うスレッドがなくなるため、接続が古くなります。これは、このエラーが長時間アイドル状態の後にのみ発生する場合に発生する可能性が最も高いです。

  2. すべての応答を完全に読み取らないと、接続がビジー状態でプールに戻される可能性があります。次回、コマンドがMySQLに送信され、間違った状態で接続が閉じられます。エラーが頻繁に発生する場合は、これが原因である可能性があります。

一方、立ち退きスレッドを設定すると、問題を軽減するのに役立ちます。このようなものをデータソースに追加します。

          ...
          removeAbandoned="true"
          removeAbandonedTimeout="120"
          logAbandoned="true"
          testOnBorrow="false"
          testOnReturn="false"
          timeBetweenEvictionRunsMillis="60000"
          numTestsPerEvictionRun="5"
          minEvictableIdleTimeMillis="30000"
          testWhileIdle="true"
          validationQuery="select now()"
于 2009-09-19T20:10:59.190 に答える
0

アイドル状態のTCP/IP接続を透過的に閉じるルーターがWebサーバーとデータベースの間にありますか?

その場合は、接続プールにXX分を超える未使用の接続をプールから破棄するか、接続をアクティブに保つためにYY分ごとに何らかのpingを実行する必要があります。

于 2009-09-19T18:22:23.567 に答える
0

あなたが答えを見つけられなかった場合、私はこれに最後の日を費やしてきました。私は基本的にあなたと同じことをしていますが、プーリングをapache.commons.poolに基づいていることを除いて. EOF が表示されているのとまったく同じエラーです。データディレクトリにある可能性が最も高いmysqldエラーログファイルを確認してください。mysqld のクラッシュを探します。mysqld_safe は、クラッシュした場合に mysqld をすぐに再起動するため、ログファイルを見ない限り、これが原因であるかどうかは明らかではありません。/var/log は、このシナリオでは役に立ちません。

クラッシュ前に作成された接続は、クラッシュ後に EOF になります。

于 2010-03-26T19:33:07.320 に答える