2014-10-09 185 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