しばらく実行した後、少なくとも20のブラウザタブが同時にサーブレットにアクセスしてサーブレットをストレステストすると、このエラーが発生します。
Java.sql.SQLException:[Tomcat-http--10]タイムアウト:プールが空です。10秒以内に接続をフェッチできません。使用できません[サイズ:200;ビジー:200;アイドル:0;ラストウェイト: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
}
おそらく、接続を保持している時間が長すぎます。
要求の処理を開始するときに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にとって問題であり、データベース側でのデータ管理/インデックス作成の改善に関連しています。
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
}
まず、メソッドの本体内でStatement
オブジェクトとResultSet
オブジェクトを閉じていません。
それらはshouldclose
でConnection
を呼び出すときにクリーンアップされます(JDBC仕様による)が、プールされた設定では、実際には取得されない可能性があります片付けた。
次に、プールされた接続のラップを解除し、基礎となる接続を返すと、すべてが切断されます。
したがって、次のようにコードを変更します。
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つのように扱ってください。
現在提供しているコードは長く複雑に見えますが、問題ありません。
ただし、「doStuff」メソッドは、より多くの接続をリークする可能性があると思います
次の前に接続を閉じてください。
RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJSP.jsp");
rd.forward(request, response);
return;
また、不要な場合は返品を削除してください。
プロセスの完了後、jdbc接続が閉じていることを確認してください。接続が閉じられていないことが原因である可能性があります。