簡體   English   中英

在Tomcat Web應用程序中使用Java和MySQL進行連接池

[英]Connection pooling with Java and MySQL in Tomcat web application

我最近編寫並部署了一個Java Web應用程序到服務器,我發現了一個在開發或測試期間沒有出現的異常問題。

當用戶在這么長時間后登錄並從數據庫中顯示數據時,該頁面表明沒有要查看的記錄。 但是在頁面刷新時,根據分頁規則顯示前x個記錄。

檢查日志,我發現:

ERROR|19 09 2009|09 28 54|http-8080-4|myDataSharer.database_access.Database_Metadata_DBA| - Error getting types of columns of tabular Dataset 12

com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception: 

** BEGIN NESTED EXCEPTION ** 

java.io.EOFException

STACKTRACE:

java.io.EOFException
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1956)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2368)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2867)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1616)

等幾百行。

該應用程序目前設置為約100個用戶,但尚未完全使用。 它使用Apache Tomcat servlets / jsps和MySQL數據庫之間的連接池,使用以下代碼示例構成數據庫操作的一般安排,其中每頁通常有幾個:

// Gets a Dataset.
public static Dataset getDataset(int DatasetNo) {
    ConnectionPool_DBA pool = ConnectionPool_DBA.getInstance();
    Connection connection = pool.getConnection();
    PreparedStatement ps = null;
    ResultSet rs = null;

    String query = ("SELECT * " +
                    "FROM Dataset " +
                    "WHERE DatasetNo = ?;");

    try {
        ps = connection.prepareStatement(query);
        ps.setInt(1, DatasetNo);
        rs = ps.executeQuery();
        if (rs.next()) {
            Dataset d = new Dataset();
            d.setDatasetNo(rs.getInt("DatasetNo"));
            d.setDatasetName(rs.getString("DatasetName"));
            ...

            }

            return d;
        }
        else {
            return null;
        }
    }
    catch(Exception ex) {
        logger.error("Error getting Dataset " + DatasetNo + "\n", ex);            
        return null;
    }
    finally {
        DatabaseUtils.closeResultSet(rs);
        DatabaseUtils.closePreparedStatement(ps);
        pool.freeConnection(connection);
    }
}

有人能夠建議糾正這個問題的方法嗎?

我認為這是由於MySQL將連接輪詢連接打開長達8小時,但我不確定。

謝謝

馬丁奧謝。


為了澄清關於我的連接池方法的一點,我在我的應用程序中使用的不是Oracle,而是我自己的類,如下所示:

package myDataSharer.database_access;

import java.sql.*;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import org.apache.log4j.Logger;

public class ConnectionPool_DBA {

    static Logger logger = Logger.getLogger(ConnectionPool_DBA.class.getName());

    private static ConnectionPool_DBA pool = null;
    private static DataSource dataSource = null;


    public synchronized static ConnectionPool_DBA getInstance() {
        if (pool == null) {
            pool = new ConnectionPool_DBA();
        }
        return pool;
    }

    private ConnectionPool_DBA() {
        try {
            InitialContext ic = new InitialContext();
            dataSource = (DataSource) ic.lookup("java:/comp/env/jdbc/myDataSharer");
        }
        catch(Exception ex) {
            logger.error("Error getting a connection pool's datasource\n", ex);
        }
    }

    public void freeConnection(Connection c) {
        try {
            c.close();
        }
        catch (Exception ex) {
            logger.error("Error terminating a connection pool connection\n", ex);           
        }
    }

    public Connection getConnection() {
        try {
            return dataSource.getConnection();
        }
        catch (Exception ex) {
            logger.error("Error getting a connection pool connection\n", ex);            
            return null;
        }
    }    
}

我認為提到Oracle是因為我使用了類似的名稱。

有一些關於避免這種情況的指針,從其他來源獲得,特別是來自其他驅動程序和其他應用程序服務器的連接池實現。 有關JNDI數據源的Tomcat文檔中已提供了一些信息。

  1. 建立一個清理/收割計划,它將關閉池中的連接,如果它們在一段時間之后處於非活動狀態。 將連接數據庫打開8小時(默認為MySQL)並不是一種好習慣。 在大多數應用程序服務器上,非活動連接超時值是可配置的,通常少於15分鍾(即連接不能在池中保留超過15分鍾,除非它們一次又一次地重復使用)。 在Tomcat中,使用JNDI DataSource時,請使用removeAbandoned和removeAbandonedTimeout設置來執行相同操作。
  2. 當新連接從池返回到應用程序時,請確保首先測試它。 例如,我知道的大多數應用程序服務器都可以進行配置,以便通過執行“SELECT 1 FROM dual”來測試與Oracle數據庫的連接。 在Tomcat中,使用validationQuery屬性為MySQL設置適當的查詢 - 我相信這是“SELECT 1”(沒有引號)。 設置validationQuery屬性的值有幫助的原因是,如果查詢無法執行,則從池中刪除連接,並在其位置創建新連接。

就應用程序的行為而言,用戶可能會看到池的結果第一次返回到應用程序的陳舊連接。 第二次,池可能返回一個可以為應用程序的查詢提供服務的不同連接。

Tomcat JNDI數據源基於Commons DBCP,因此適用於DBCP的配置屬性也適用於Tomcat。

我想知道為什么你在代碼中使用ConnectionPool_DBA而不是讓Tomcat處理池並簡單地使用JNDI查找連接。

為什么使用MySQL連接池? 當我進行JNDI查找和連接池時,我更喜歡Apache DBCP庫。 我發現它運作良好。

我還問你的DatabaseUtils方法是否會拋出任何異常,因為如果在調用pool.freeConnection()之前的任何一個調用拋出一個,你將永遠不會釋放該連接。

我不太喜歡你的代碼,因為執行SQL操作的類應該將Connection實例傳遞給它,並且不應該具有獲取和使用Connection的雙重責任。 持久化類無法知道它是否在更大的事務中使用。 最好有一個單獨的服務層來獲取Connection,管理事務,編組持久化類,並在完成時進行清理。

更新:

Google提出了與您同名的Oracle類。 現在我真的不喜歡你的代碼,因為當你可以輕松獲得更好的替代品時,你自己寫了一些東西。 我馬上把你丟棄,用DBCP和JNDI重做。

此錯誤表示服務器意外關閉連接。 這可能發生在以下2個案例中,

  1. MySQL在一定時間后關閉空閑連接(默認為8小時)。 發生這種情況時,沒有線程負責關閉連接,因此它變得陳舊。 如果此錯誤僅在長時間空閑后發生,則很可能是這種情況。

  2. 如果您沒有完全讀取所有響應,則連接可能會在忙碌狀態下返回到池中。 下一次,命令被發送到MySQL並關閉錯誤狀態的連接。 如果錯誤發生頻繁,這可能是原因。

同時,設置逐出線程將有助於緩解問題。 將這樣的內容添加到數據源,

          ...
          removeAbandoned="true"
          removeAbandonedTimeout="120"
          logAbandoned="true"
          testOnBorrow="false"
          testOnReturn="false"
          timeBetweenEvictionRunsMillis="60000"
          numTestsPerEvictionRun="5"
          minEvictableIdleTimeMillis="30000"
          testWhileIdle="true"
          validationQuery="select now()"

Web服務器和數據庫之間是否存在透明地關閉空閑TCP / IP連接的路由器?

如果是這樣,您必須讓連接池丟棄池中未使用的超過XX分鍾的連接,或者在連接上每隔YY分鍾執行某種ping操作以使其保持活動狀態。

如果你沒有找到答案,那么我最后一天一直在處理這個問題。 我基本上都在做同樣的事情,除了我的基礎是apache.commons.pool。 你看到EOF的確切錯誤。 檢查最有可能在數據目錄中的mysqld錯誤日志文件。 尋找mysqld崩潰。 mysqld_safe會在崩潰時快速重新啟動你的mysqld,所以除非你查看它的日志文件,否則不會發生這種情況。 / var / log對此方案沒有幫助。

崩潰之前創建的連接將在崩潰后進行EOF。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM