10

しばらく実行した後、少なくとも20個のブラウザタブが同時にサーブレットにアクセスしてサーブレットをストレステストすると、このエラーが発生します。

java.sql.SQLException:[tomcat-http--10]タイムアウト:プールが空です。10秒以内に接続をフェッチできません。使用できません[サイズ:200; 忙しい:200; アイドル:0; lastwait:10000]。

このためのXML構成は次のとおりです。

<Resource name="jdbc/MyAppHrd"
          auth="Container"
          type="javax.sql.DataSource"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          testWhileIdle="true"
          testOnBorrow="true"
          testOnReturn="false"
          validationQuery="SELECT 1"
          validationInterval="30000"
          timeBetweenEvictionRunsMillis="30000"
          maxActive="200"
          minIdle="10"
          maxWait="10000"
          initialSize="200"
          removeAbandonedTimeout="120"
          removeAbandoned="true"
          logAbandoned="false"
          minEvictableIdleTimeMillis="30000"
          jmxEnabled="true"
          jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
            org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
          username="sa"
          password="password"
          driverClassName="net.sourceforge.jtds.jdbc.Driver"
          url="jdbc:jtds:sqlserver://192.168.114.130/MyApp"/>

何が問題なのですか?

更新:Javaコード:

public class MyServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final Log LOGGER = LogFactory.getLog(MyServlet.class);

   private void doRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

        CallableStatement stmt = null;
        ResultSet rs = null;

        Connection conn = null;
        try {

            conn = getConnection();

            stmt = conn.prepareCall("{call sp_SomeSPP(?)}");
            stmt.setLong(1, getId());

            rs = stmt.executeQuery();

            // set mime type
            while (rs.next()) {
                if (rs.getInt(1)==someValue()) {
                    doStuff();
                    break;
                }
            }
            stmt = conn.prepareCall("{call sp_SomeSP(?)}");
            stmt.setLong(1, getId());

            rs = stmt.executeQuery();
            if (rs.next()) {
                // do stuff
            }

            RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJSP.jsp");
            rd.forward(request, response);
            return;
        } catch (NamingException e) {
            LOGGER.error("Database connection lookup failed", e);
        } catch (SQLException e) {
            LOGGER.error("Query failed", e);
        } catch (IllegalStateException e) {
            LOGGER.error("View failed", e);
        } finally {
            try {
                if (rs!=null && !rs.isClosed()) {
                    rs.close(); 
                }
            } catch (NullPointerException e) {
                LOGGER.error("Result set closing failed", e);
            } catch (SQLException e) {
                LOGGER.error("Result set closing failed", e);
            }
            try {
                if (stmt!=null) stmt.close();
            } catch (NullPointerException e) {
                LOGGER.error("Statement closing failed", e);
            } catch (SQLException e) {
                LOGGER.error("Statement closing failed", e);
            }
            try {
                if (conn != null){
                    conn.close();
                    conn = null;
                }
            } catch (NullPointerException e) {
                LOGGER.error("Database connection closing failed", e);
            } catch (SQLException e) {
                LOGGER.error("Database connection closing failed", e);
            }
        }

   }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doRequest(request, response);
    }

    protected static Connection getConnection() throws NamingException, SQLException {
        InitialContext cxt = new InitialContext();
        String jndiName = "java:/comp/env/jdbc/MyDBHrd";
        ConnectionPoolDataSource dataSource = (ConnectionPoolDataSource) cxt.lookup(jndiName);
        PooledConnection pooledConnection = dataSource.getPooledConnection();
        Connection conn = pooledConnection.getConnection();
        return conn; // Obtain connection from pool
    }   
4

6 に答える 6

5

getConnectionメソッドを次のように変更することをお勧めします。実際には、javax.sql.PooledConnectionインターフェイスを介して直接アクセスしてプーリングサポートを削除している可能性があります。

        InitialContext cxt = new InitialContext();
        String jndiName = "java:/comp/env/jdbc/MyDBHrd";
        DataSource dataSource = (DataSource) cxt.lookup(jndiName);
        return dataSource.getConnection();

また、DBUtils#closeQuietlyなどを使用して接続をクリーンアップします

更新:接続からプーリングサポートを削除します。以下を実行して出力を見ると、データソースから直接取得された接続が、PooledConnectionをラップするProxyConnectionであることがわかります。

public static void main(String[] args) throws Exception {

    Properties properties = new Properties();
    properties.put("username", "sa");
    properties.put("password", "password");
    properties.put("driverClassName", "net.sourceforge.jtds.jdbc.Driver");
    properties.put("url", "jdbc:jtds:sqlserver://192.168.114.130/MyApp");       

    DataSourceFactory dsFactory = new DataSourceFactory();      
    DataSource ds = dsFactory.createDataSource(properties);     
    ConnectionPoolDataSource cpds = (ConnectionPoolDataSource) ds;
    PooledConnection pooledConnection = cpds.getPooledConnection();

    System.out.println("Pooled Connection - [" + ds.getConnection() + "]"); // Close will return to the Pool
    System.out.println("Internal Connection - [" + pooledConnection.getConnection() + "]"); // Close will just close the connection and not return to pool

}
于 2012-12-20T17:32:32.037 に答える
4

おそらく、接続を保持している時間が長すぎます。

要求の処理を開始するときにDB接続を開かず、最終的に応答をコミットしたときに解放するようにしてください。

典型的な間違いは次のとおりです。

    @Override
    protected void doGet (
            final HttpServletRequest request,
            final HttpServletResponse response
        ) throws
            ServletException,
            IOException
    {
        Connection conn = myGetConnection( );

        try
        {
            ...
            // some request handling


        }
        finally
        {
            conn.close( )
        }
    }

このコードでは、データベース接続の有効期間は、サーバーに接続されているクライアントに完全に委ねられています。

より良いパターンは

    @Override
    protected void doGet (
            final HttpServletRequest request,
            final HttpServletResponse response
        ) throws
            ServletException,
            IOException
    {
        // some request preprocessing
        MyProcessedRequest parsedInputFromRequest =
            getInputFromRequest( request );

        final MyModel model;
        {
           // Model generation
           Connection conn = myGetConnection( );

           try
           {
              model = new MyModel( conn, parsedInputFromRequest );
           }
           finally
           {
              conn.close( );
           }
        }


        generateResponse( response, model );         
    }

ボトルネックがモデル生成にある場合でも、接続が不足することに注意してください。ただし、これはDBAにとって問題であり、データベース側でのデータ管理/インデックス作成の改善に関連しています。

于 2012-12-20T16:42:28.140 に答える
0

まず、メソッドの本体内でオブジェクトをStatement閉じていません。ResultSet

(JDBC仕様に従って)を呼び出すときにクリーンアップする必要がありますが、プールされた設定では、実際にはクリーンアップされない場合がありますcloseConnection

次に、プールされた接続のラップを解除し、基礎となる接続を返します。これにより、すべてが切断されます。

したがって、次のようにコードを変更します。

 try {
        conn = getConnection();

        stmt = conn.prepareCall("{call sp_SomeSPP(?)}");
        stmt.setLong(1, getId());

        rs = stmt.executeQuery();

        // set mime type
        while (rs.next()) {
            if (rs.getInt(1)==someValue()) {
                doStuff();
                break;
            }
        }

        // ADD THESE LINES
        rs.close(); rs = null;
        stmt.close(); stmt = null;

        stmt = conn.prepareCall("{call sp_SomeSP(?)}");
        stmt.setLong(1, getId());

        rs = stmt.executeQuery();
        if (rs.next()) {
            // do stuff
        }
}

....

protected static Connection getConnection() throws NamingException, SQLException {
    InitialContext cxt = new InitialContext();
    String jndiName = "java:/comp/env/jdbc/MyDBHrd";
    DataSource dataSource = (DataSource) cxt.lookup(jndiName);
    return dataSource.getPooledConnection();
}   

そして、他の人が言っているように、別のページに転送するようなことをする前に、あなたは間違いなくあなたのリソースをクリーンアップしたいと思うでしょう。それ以外の場合は、接続を必要以上に長く維持します。これは重要なリソースです。1つのように扱ってください。

于 2012-12-20T18:53:57.307 に答える
0

プロセスの完了後、jdbc接続が閉じていることを確認してください。接続が閉じられていないことが原因である可能性があります。

于 2012-12-20T16:34:23.160 に答える
0

次の前に接続を閉じてください。

RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJSP.jsp");
        rd.forward(request, response);
        return;

また、不要な場合は返品を削除してください。

于 2012-12-20T16:47:36.180 に答える
0

現在提供しているコードは長く/複雑に見えますが、問題ありません。

ただし、「doStuff」メソッドは、より多くの接続をリークする可能性があると思います

于 2012-12-20T17:13:57.970 に答える