Tomcat連接池配置

今日做了個小網站,數據量不大,但當發佈到虛擬主機上之後,接連不斷的遇到各種問題。

被折磨了數日後,在網上查了大量的相關資料,現總結如下。

一.項目在上傳到遠程服務器的過程中,有可能丟失文件,或文件內容發生改變。雖然機率是很小的,但程序容不得一丁點錯誤,你懂得。。。

方法一般爲:將程序打成war包上傳,或將編譯好的項目打個壓縮包(如tomcat根目錄下的項目文件)上傳。項目完成後,一定要在本地測試確保無誤,把本地測試過的傳上去。否則,不知不覺中就會出錯,而且不知道哪出錯,沒法調試。

二.配置tomcat虛擬主機連接池。

  • 在tomcat配置文件server.xml下,找到
  • <Context path="" docBase="" reloadable="true" />
    這一行,並改爲
    <Context path="" docBase="" reloadable="true">
    <Resource name="jdbc/sxdDS" auth="Container"
                  type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver"
                  url="jdbc:mysql://localhost:3306/數據庫名?characterEncoding=GBK&amp;useUnicode=true"
                  username="用戶" password="密碼" maxActive="100" maxIdle="10"
                  maxWait="-1" removeAbandoned="true" removeAbandonedTimeout="20" logAbandoned="true"/>
    </Context>
  • 相關參數說明:
  • dataSource: 要連接的 datasource (通常我們不會定義在 server.xml)
    defaultAutoCommit: 對於事務是否 autoCommit, 默認值爲 true
    defaultReadOnly: 對於數據庫是否只能讀取, 默認值爲 false
    driverClassName:連接數據庫所用的 JDBC Driver Class,
    maxActive: 可以從對象池中取出的對象最大個數,爲0則表示沒有限制,默認爲8
    maxIdle: 最大等待連接中的數量,設 0 爲沒有限制 (對象池中對象最大個數)
    minIdle:對象池中對象最小個數
    maxWait: 最大等待秒數, 單位爲 ms, 超過時間會丟出錯誤信息,-1爲無限制
    password: 登陸數據庫所用的密碼
    url: 連接數據庫的 URL
    username: 登陸數據庫所用的帳號
    validationQuery: 驗證連接是否成功, SQL SELECT 指令至少要返回一行
    removeAbandoned: 是否自我中斷, 默認是 false
    removeAbandonedTimeout: 幾秒後會自我中斷, removeAbandoned 必須爲 true
    logAbandoned: 是否記錄中斷事件, 默認爲 false
    minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證;默認30分鐘
    timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程,默認-1
    testOnBorrow:取得對象時是否進行驗證,檢查對象是否有效,默認爲false
    testOnReturn:返回對象時是否進行驗證,檢查對象是否有效,默認爲false
    testWhileIdle:空閒時是否進行驗證,檢查對象是否有效,默認爲false
    Ø 在使用DBCP的時候,如果使用默認值,則數據庫連接因爲某種原因斷掉後,再從連接池中取得連接又不進行驗證,這時取得的連接實際上就會是無效的數據庫連接。因此爲了防止獲得的數據庫連接失效,在使用的時候最好保證:
    username: 登陸數據庫所用的帳號
    validationQuery:SELECT COUNT(*) FROM DUAL
    testOnBorrow、testOnReturn、testWhileIdle:最好都設爲true
    minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證
    timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程
    Ø PS:在構造GenericObjectPool [BasicDataSource在其createDataSource () 方法中也會使用GenericObjectPool]時,會生成一個內嵌類Evictor,實現自Runnable接口。如果 timeBetweenEvictionRunsMillis大於0,每過timeBetweenEvictionRunsMillis毫秒 Evictor會調用evict()方法,檢查對象的閒置時間是否大於minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小於等於0時則忽略,默認爲30分鐘),是則銷燬此對象,否則就激活並校驗對象,然後調用 ensureMinIdle方法檢查確保池中對象個數不小於_minIdle。在調用returnObject方法把對象放回對象池,首先檢查該對象是否有效,然後調用PoolableObjectFactory的passivateObject方法使對象處於非活動狀態。再檢查對象池中對象個數是否小於 maxIdle,是則可以把此對象放回對象池,否則銷燬此對象
    Ø 上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證

    在Tomcat的Server.xml,我們可以看看下面的這個例子:

    <Resource name="lda/raw"
                  type="javax.sql.DataSource"
                   password="lda_master"
                   driverClassName="oracle.jdbc.driver.OracleDriver"
                   maxIdle="30" minIdle="2" maxWait="60000" maxActive="1000"
                   testOnBorrow="true" testWhileIdle="true" validationQuery="select 1 from dual"
                   username="lda_master" url="jdbc:oracle:thin:@192.160.100.107:15537:lcststd"/>

    這樣的話,就可以避免產生Connection Reset的錯誤了.

    這樣一來,就能夠解決Connect Reset的問題了。剛纔說了,其實很多App Server都會有相應的配置地方,只是大型的服務器正好提供了Admin Console,上面可以顯式的配置Connection Pool,也有明顯的屬性選擇。
  • 值得一提的是,
  • removeAbandoned: 是否自我中斷, 默認是 false
    removeAbandonedTimeout: 幾秒後會自我中斷, removeAbandoned 必須爲 true
    logAbandoned: 是否記錄中斷事件, 默認爲 false
    這三個屬性。

removeAbandoned removeAbandonedTimeout 兩個屬性配合使用可以將指定時間內沒有關閉的connection回收關閉。

設置了這幾個屬性後,連接池連接數的狀態是這樣的,如果存在沒有關閉的連接,連接池會每隔removeAbandonedTimeout設置的時間,檢測一下連接池的連接數和連接狀態,如果當連接池中活動的連接數大於maxActive設置的最大連接數時,將會啓動連接回收,這個連接回收是不會將連接回收到連接池重複利用,而是直接銷燬這些連接。在這個時候連接池的連接數會突然下降至當前需要的連接,而這些連接是連接池重新產生的,不是回收利用的。

如果持續發現這種情況,而且這些連接在數據庫總的狀態一直是sleep狀態,我個人覺得,就應該是程序中沒有正常關閉連接了。我們就需要在程序中找到這個連接。恰恰連接池提供了logAbandoned屬性,如果將其設置爲true,那麼在出現上述情況時,關閉的連接信息打印到日誌,類似的打印日誌如下:

DBCP object created 2011-04-14 11:22:18 by the following code was never closed:

java.lang.Exception

        at org.apache.tomcat.dbcp.dbcp.AbandonedTrace.setStackTrace(AbandonedTrace.java:160)

        at org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool.borrowObject(AbandonedObjectPool.java:86)

        at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:96)

        at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:880)

        at org.yeeda.costexpress.util.ConnectionPool.getConnection(Unknown Source)

        at org.yeeda.costexpress.service.member.impl.MyMaterialServiceImpl.exportExcel(Unknown Source)

        at org.yeeda.costexpress.servlet.member.MyMaterialServlet.toExcel(Unknown Source)

        at org.yeeda.costexpress.servlet.member.MyMaterialServlet.doPost(Unknown Source)

這個相當於異常處理,程序不會報錯。但是我們可以通過異常信息找到到底是哪裏的connection沒有關閉,能夠找到代碼的位置,然後仔細分析代碼,看從連接池的拿到的連接到底執行了close()方法沒有。如果出現上述那樣的情況,一般是沒有關閉的。

三、在應用程序的web.xml文件中,</web-app>標籤前,添加如下代碼:

<resource-ref> 
 <description>sxd Datasource example</description> 
 <res-ref-name>jdbc/sxdDS</res-ref-name> 
 <res-type>javax.sql.DataSource</res-type> 
 <res-auth>Container</res-auth> 
 </resource-ref> 
 四、程序中的java代碼,DBConnection.java。

 

public class DBConnetion {
 
 private Connection conn = null;
 private PreparedStatement psta = null;

 public Connection getConnection() {
 
   try {
    
     Context cxt = new InitialContext();
     DataSource ds=(DataSource) cxt.lookup("java:comp/env/jdbc/sxdDS");
     conn=ds.getConnection();
    
    
   } catch (NamingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } 
  return conn;
 }

 public PreparedStatement getPsta(String sql) {
  conn = this.getConnection();// 得到連接
  try {
   psta = conn.prepareStatement(sql);
  } catch (SQLException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  return psta;
 }

 /**
  * 關閉Connetion連接
  * @param conn
  * @throws SQLException
  * @throws SQLException
  */
 public void closeConnetion(Connection conn){
  if (conn != null) {
   try {
    conn.close();//使用連接池後,colse()被重寫,此時並沒關閉,而是放回了連接池中
    //System.out.println("連接是否關閉: "+conn.isClosed());
    conn = null;
   } catch (SQLException e) {
    e.printStackTrace();
   }finally {
    if(conn!=null){
     try {
      conn.close() ;
      System.out.println("連接是否關閉: "+conn.isClosed());
     } catch (SQLException e) {
      e.printStackTrace();
     }
    }
   }
  }
 }

 /**
  * 關閉所有連接
  * @param conn
  * @param psta
  * @param rs
  */
 public void closeAll(Connection conn, PreparedStatement psta, ResultSet rs) {
  try {
   
   if (rs != null) {
    rs.close();
    rs = null;
   }
   if (psta != null) {
    psta.close();
    psta = null;
   }
   if (conn != null) {
    this.closeConnetion(conn);
   }
  } catch (SQLException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

}

 

 相關配置的官方資料:

http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html

http://commons.apache.org/dbcp/configuration.html

發佈了14 篇原創文章 · 獲贊 48 · 訪問量 52萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章