2014-10-09 190 views
1

我们正在开发使用Tomcat的连接池和空闲连接

  • 的Tomcat 7
  • JDBC
  • 的PostgreSQL 9.2

我们已经有一些连接泄漏,并认为我们纠正他们的网站(数据库不再停止响应),但连接池的行为似乎仍然存在漏洞,因为我们有大量空闲连接大于context.xml中设置的maxIdle。我想确定问题已解决。

出于测试目的,我使用了以下的context.xml:

<Resource 
    auth="Container" 
    name="jdbc/postgres" 
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
    type="javax.sql.DataSource" 

    username="admin" 
    password="..." 

    driverClassName="org.postgresql.Driver" 
    url="jdbc:postgresql://127.0.0.1:5432/..." 
    initialSize="1" 
    maxActive="50" 
    minIdle="0" 
    maxIdle="3" 
    maxWait="-1" 
    minEvictableIdleTimeMillis="1000" 
    timeBetweenEvictionRunsMillis="1000" 
    /> 

如果我理解正确的,我们应该在启动1个空闲连接,并从0至3取决于负载,对不对?

发生的情况是:启动时连接1个,负载低时最多3个空闲连接,高负载后多于3个空闲连接。然后,这些连接不立即关闭,我们不知道何时/是否它们将被关闭(有时它们中的一些关闭)。

所以问题是:这种行为是正常的,还是不正常?

感谢您的帮助

编辑:增加工厂属性,并没有改变问题

编辑2:使用removeAbandoned & removeAbandonedTimeout使闲置CONNEXIONS被每逢removeAbandonedTimeout。所以我们可能还有一些连接泄漏。下面是一些代码片段,我们使用连接到数据库和执行请求:

PostgreSQLConnectionProvider,只是一个静态类提供连接:

public class PostgreSQLConnectionProvider { 

    public static Connection getConnection() throws NamingException, SQLException { 

     String dsString = "java:/comp/env/jdbc/postgres"; 
     Context context = new InitialContext(); 
     DataSource ds = (DataSource) context.lookup(dsString); 
     Connection connection = ds.getConnection(); 

     return connection; 
    } 
} 

DAO抽象类:

public abstract class DAO implements java.lang.AutoCloseable { 

    // Private attributes : 
    private Connection _connection; 

    // Constructors : 
    public DAO() { 

     try { _connection = PostgreSQLConnectionProvider.getConnection(); } 
     catch (NamingException | SQLException ex) { 
      Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    // Getters : 
    public Connection getConnection() { return _connection; } 

    // Closeable : 
    @Override 
    public void close() throws SQLException { 

     if(!_connection.getAutoCommit()) { 

      _connection.rollback(); 
      _connection.setAutoCommit(true); 
     } 

     _connection.close(); 
    } 
} 

的UserDAO,小DAO子类(我们有几个DAO sublasses请求数据库):

public class UserDAO extends DAO { 

    public User getUserWithId(int id) throws SQLException { 

     PreparedStatement ps = null; 
     ResultSet rs = null; 

     User user = null; 

     try { 

      String sql = "select * from \"USER\" where id_user = ?;"; 

      ps = getConnection().prepareStatement(sql); 
      ps.setInt(1, id); 

      rs = ps.executeQuery(); 
      rs.next(); 

      String login = rs.getString("login"); 
      String password = rs.getString("password"); 
      String firstName = rs.getString("first_name"); 
      String lastName = rs.getString("last_name"); 
      String email = rs.getString("email"); 

      user = new User(id, login, password, firstName, lastName, email); 
     } 
     finally { 

      if(rs != null) rs.close(); 
      if(ps != null) ps.close(); 
     } 

     return user; 
    } 
} 

一个DAO子类中使用的一个例子:

try(UserDAO dao = new UserDAO()) { 

    try { 

     User user = dao.getUserWithId(52); 
    } 
    catch (SQLException ex) { 

     // Handle exeption during getUserWithId 
    } 
} 
catch (SQLException ex) { 

    // Handle exeption during dao.close() 
} 
+0

尝试使用tomcat的连接池实现 - 功能更丰富,并且具有良好的文档https://people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html – 2014-10-09 16:35:51

+0

谢谢,但它已经我在做什么(你可以在context.xml中看到我使用的参数)。 – 2014-10-09 18:57:23

+0

我看不到主要部分 - “工厂”属性和适当的类名称 – 2014-10-09 19:09:57

回答

1

综观代码看起来在DAO的生命周期中连接被抓取,而不是声明的生命周期,这是通常的期望。通常情况下,您可以像执行语句一样从池中获取连接,并在完成后调用close()以将其返回池。

此外,在您的finally子句中,rs.close()ps.close()都会抛出异常,导致缺少对准备语句的最后一次调用。

在Java 7中,您还可以使用try with resources语句来关闭准备好的语句和连接。根据规范,当声明关闭时,司机应该为你关闭结果。

+0

感谢您的回答。是的,连接在DAO的生命周期中被捕获,但DAO在几行后关闭,我不认为这是一个问题,是吗?可以抛出异常的close(),我将它们封装在try-catch中(我更喜欢自己调用rs.close()而不是依靠驱动的实现)。我不能在下周之前测试,我会让你知道close()包装是否改变了某些东西 – 2014-10-15 07:13:35

+0

如果你为getUserWithId()的每个调用实例化DAO,那么是的,它应该没问题。 – 2014-10-17 17:35:33