经过一段时间的运行后,当我用至少20个浏览器标签同时访问servlet来测试我的servlet时,我遇到了这个错误:
java.sql.SQLException:[tomcat-http - 10]超时:池为空。无法在10秒内获取连接,无法使用[size: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
}
答案 0 :(得分:5)
我建议您将getConnection方法更改为以下内容,您可能实际上是通过直接通过javax.sql.PooledConnection接口删除Pooling支持
InitialContext cxt = new InitialContext();
String jndiName = "java:/comp/env/jdbc/MyDBHrd";
DataSource dataSource = (DataSource) cxt.lookup(jndiName);
return dataSource.getConnection();
还可以使用类似DBUtils#closeQuietly的内容来清理连接
更新:您正在从Connection中删除Pooling支持。如果运行以下命令并查看输出,您将看到直接从DataSource检索的连接是包含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
}
答案 1 :(得分:3)
可能你连接的时间太长了。
确保在开始处理请求时不打开数据库连接,然后在最终提交响应时将其释放。
典型的错误是:
@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来说是一个问题,这与数据库端更好的数据管理/索引有关。
答案 2 :(得分:0)
检查完成进程后您的jdbc连接是否已关闭。它可能是由未关闭的连接引起的。
答案 3 :(得分:0)
在以下情况之前关闭您的连接。
RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJSP.jsp");
rd.forward(request, response);
return;
如果不需要,也请删除退货。
答案 4 :(得分:0)
您目前提供的代码看起来很长/很复杂,但很好。
但是,我猜你的“doStuff”方法可能是泄漏更多连接的候选者
答案 5 :(得分:0)
首先,您没有关闭方法正文中的Statement
和ResultSet
个对象。
close
上调用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();
}
而且,正如其他人所说,你肯定想在
之前清理你的资源你做的事情,比如转发到另一个页面。否则,保持连接的时间远远超过必要的时间。这是一个关键资源:像对待它一样。