我们自己实现连接池,但为什么它总是没有连接?

时间:2009-10-20 04:42:03

标签: java database connection-pooling

以下是源代码:

//public class ConnectionPool implements Runnable
public class ConnectionPool {
    private static Logger loger_error = Logger.getLogger("error");
    // JDBC Driver name
    String driverName;

    // JDBC Connection URL
    String connectionURL;

    // Minimum size of the pool
    int connectionPoolSize;

    // Maximum size of the pool
    int connectionPoolMax;

    // Maximum number of uses for a single connection, or -1 for none
    int connectionUseCount;

    // Maximum connection idle time (in minutes)
    int connectionTimeout;

    // Additional JDBC properties
    String userName;

    String password;

    // The Connection pool. This is a vector of ConnectionObject
    // objects
    Vector pool;

    // The maximum number of simultaneous connections as reported
    // by the JDBC driver
    int maxConnections = -1;

    // Scheduler scheduler;

    // Timeout value
    public static int TIMEOUT_MS = 20000;

    /**
     * Initializes the ConnectionPool object using 'ConnectionPool.cfg' as the
     * configuration file
     * 
     * @return true if the ConnectionPool was initialized properly
     */
    /*
     * public boolean initialize() throws Exception { return
     * initialize("com/omh/jdbc/ConnectionPool.cfg"); }
     */

    /**
     * Initializes the ConnectionPool object with the specified configuration
     * file
     * 
     * @param config
     *            Configuration file name
     * @return true if the ConnectionPool was initialized properly
     */
    public void initialize(String driverName, String connectionURL,
            int connectionPoolSize, int connectionPoolMax,
            int connectionUseCount, int connectionTimeout, String userName,
            String password) throws Exception {
        this.driverName = driverName;
        this.connectionURL = connectionURL;
        this.connectionPoolSize = connectionPoolSize;
        this.connectionPoolMax = connectionPoolMax;
        this.connectionUseCount = connectionUseCount;
        this.connectionTimeout = connectionTimeout;
        this.userName = userName;
        this.password = password;

        createPool();

        // scheduler = new Scheduler();
        // scheduler.schedule(this, TIMEOUT_MS);

    }

    /**
     * Destroys the pool and it's contents. Closes any open JDBC connections and
     * frees all resources
     */
    public void destroy() {
        try {
            // Clear our pool
            if (pool != null) {
                // Loop throught the pool and close each connection
                for (int i = 0; i < pool.size(); i++) {
                    ((MangoDBConnection) pool.elementAt(i)).closeConnection();
                }
            }
            pool = null;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Gets an available JDBC Connection. Connections will be created if
     * necessary, up to the maximum number of connections as specified in the
     * configuration file.
     * 
     * @return JDBC Connection, or null if the maximum number of connections has
     *         been exceeded
     */
    public synchronized MangoDBConnection getConnection() {
        // If there is no pool it must have been destroyed
        if (pool == null) {
            return null;
        }

        MangoDBConnection connectionObject = null;
        int poolSize = pool.size();

        // Get the next available connection
        for (int i = 0; i < poolSize; i++) {
            // Get the ConnectionObject from the pool
            MangoDBConnection co = (MangoDBConnection) pool.elementAt(i);

            // If this is a valid connection and it is not in use,
            // grab it
            if (co.isAvailable()) {
                connectionObject = co;
                break;
            }
        }

        // No more available connections. If we aren't at the
        // maximum number of connections, create a new entry
        // in the pool
        if (connectionObject == null) {
            if ((connectionPoolMax < 0)
                    || ((connectionPoolMax > 0) && (poolSize < connectionPoolMax))) {
                // Add a new connection.
                int i = addConnection();

                // If a new connection was created, use it
                if (i >= 0) {
                    connectionObject = (MangoDBConnection) pool.elementAt(i);
                }
            } else {
                LogManager.log("Maximum number of connections exceeded");
                loger_error.error("Maximum number of connections exceeded");
            }
        }

        // If we have a connection, set the last time accessed,
        // the use count, and the in use flag
        if (connectionObject != null) {
            connectionObject.use();
            connectionObject.touch();
        }

        return connectionObject;
    }

    /**
     * Places the connection back into the connection pool, or closes the
     * connection if the maximum use count has been reached
     * 
     * @param Connection
     *            object to close
     */

    public synchronized void releaseConnection(MangoDBConnection con) {
        removeFromPool(con);
    }

    public synchronized void release(MangoDBConnection con) {
        if ((connectionUseCount > 0)
                && (con.getUseCount() >= connectionUseCount)) {
            removeFromPool(con);
            // add new connection upon releasing one
            addConnection();
        } else {
            con.touch();
            con.free();
        }
        /*
         * // Find the connection in the pool int index = find(con);
         * System.out.println("close"); if (index != -1) { ConnectionObject co =
         * (ConnectionObject) pool.elementAt(index);
         *  // If the use count exceeds the max, remove it from // the pool. if
         * ((connectionUseCount > 0) && (co.useCount >= connectionUseCount)) {
         * trace("Connection use count exceeded"); removeFromPool(index); } else { //
         * Clear the use count and reset the time last used co.touch();
         * co.free(); } }
         */
    }

    /**
     * Prints the contents of the connection pool to the standard output device
     */
    public void printPool() {
        printPool(new PrintWriter(System.out));
    }

    /**
     * Prints the contents of the connection pool to the given PrintWriter
     */
    public void printPool(PrintWriter out) {
        out.println("--ConnectionPool--");
        if (pool != null) {
            for (int i = 0; i < pool.size(); i++) {
                MangoDBConnection co = (MangoDBConnection) pool.elementAt(i);
                out.println("" + i + "=" + co);
            }
        }
    }

    /**
     * Returns an enumeration of the ConnectionObject objects that represent the
     * pool
     */
    public Enumeration getConnectionPoolObjects() {
        return pool.elements();
    }

    public int returnConnectionCount() {
        return connectionUseCount;
    }

    public int returnMaxPoolSize() {
        return connectionPoolMax;
    }

    public int returnInitPoolSize() {
        return connectionPoolSize;
    }

    /**
     * Removes the ConnectionObject from the pool at the given index
     * 
     * @param index
     *            Index into the pool vector
     */
    private synchronized void removeFromPool(MangoDBConnection con) {
        // Make sure the pool and index are valid
        if (pool != null) {
            con.closeConnection();
            pool.removeElement(con);
        }
    }

    /**
     * Creates the initial connection pool. A timer thread is also created so
     * that connection timeouts can be handled.
     * 
     * @return true if the pool was created
     */
    private void createPool() throws Exception {
        // Dump the parameters we are going to use for the pool.
        // We don't know what type of servlet environment we will
        // be running in - this may go to the console or it
        // may be redirected to a log file
        LogManager.log("JDBCDriver = " + driverName);
        LogManager.log("JDBCConnectionURL = " + connectionURL);
        LogManager.log("ConnectionPoolSize = " + connectionPoolSize);
        LogManager.log("ConnectionPoolMax = " + connectionPoolMax);
        LogManager.log("ConnectionUseCount = " + connectionUseCount);
        LogManager.log("ConnectionTimeout = " + connectionTimeout + " seconds");
        LogManager.log("Registering " + driverName);

        Driver d = (Driver) Class.forName(driverName).newInstance();

        // Create the vector for the pool
        pool = new Vector();

        // Bring the pool to the minimum size
        fillPool(connectionPoolSize);
    }

    /**
     * Adds a new connection to the pool
     * 
     * @return Index of the new pool entry, or -1 if an error has occurred
     */
    public int addConnection() {
        int index = -1;

        try {
            // Calculate the new size of the pool
            int size = pool.size() + 1;

            // Create a new entry
            fillPool(size);

            // Set the index pointer to the new connection if one
            // was created
            if (size == pool.size()) {
                index = size - 1;
            }
        } catch (Exception ex) {
            System.out.println("SSSSSSS");
            ex.printStackTrace();
        }
        return index;
    }

    /**
     * Brings the pool to the given size
     */
    private synchronized void fillPool(int size) throws Exception {
        String userID = this.userName;
        String password = this.password;

        // userID = getPropertyIgnoreCase(JDBCProperties, "user");
        // password = getPropertyIgnoreCase(JDBCProperties, "password");

        // Loop while we need to create more connections
        while (pool.size() < size) {
            MangoDBConnection co = new MangoDBConnectionMSSQL();

            // Create the connection
            co.makeConnection(connectionURL, userID, password);

            // Do some sanity checking on the first connection in
            // the pool
            if (pool.size() == 0) {
                // Get the maximum number of simultaneous connections
                // as reported by the JDBC driver
                maxConnections = co.getMaxConnections();
            }

            // Give a warning if the size of the pool will exceed
            // the maximum number of connections allowed by the
            // JDBC driver
            if ((maxConnections > 0) && (size > maxConnections)) {
                LogManager
                        .log("WARNING: Size of pool will exceed safe maximum of "
                                + maxConnections);
            }

            // Clear the in use flag
            co.free();
            // Set the last access time
            co.touch();

            pool.addElement(co);
        }

    } // fillPool()

    /**
     * Gets a the named propery, ignoring case. Returns null if not found
     * 
     * @param p
     *            The property set
     * @param name
     *            The property name
     * @return The value of the propery, or null if not found
     */
    private String getPropertyIgnoreCase(Properties p, String name) {
        if ((p == null) || (name == null))
            return null;

        String value = null;

        // Get an enumeration of the property names
        Enumeration enumeration = p.propertyNames();

        // Loop through the enum, looking for the given property name
        while (enumeration.hasMoreElements()) {
            String pName = (String) enumeration.nextElement();
            if (pName.equalsIgnoreCase(name)) {
                value = p.getProperty(pName);
                break;
            }
        }

        return value;
    }

    /**
     * Called by the timer each time a clock cycle expires. This gives us the
     * opportunity to timeout connections
     */
    /*
     * public synchronized void run() { // No pool means no work if (pool ==
     * null) { return; }
     *  // Get the current time in milliseconds long now =
     * System.currentTimeMillis();
     *  // Check for any expired connections and remove them for (int i =
     * pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject)
     * pool.elementAt(i);
     *  // If the connection is not in use and it has not been // used recently,
     * remove it if (!co.inUse) { if ((connectionTimeout > 0) && (co.lastAccess +
     * (connectionTimeout * 1000) < now)) { removeFromPool(i); } } }
     *  // Remove any connections that are no longer open for (int i =
     * pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject)
     * pool.elementAt(i); try { // If the connection is closed, remove it from
     * the pool if (co.con.isClosed()) { trace("Connection closed
     * unexpectedly"); removeFromPool(i); } } catch (Exception ex) { } }
     *  // Now ensure that the pool is still at it's minimum size try { if (pool !=
     * null) { if (pool.size() < connectionPoolSize) {
     * fillPool(connectionPoolSize); } } } catch (Exception ex) {
     * ex.printStackTrace(); }
     *  // Reschedule ourselves scheduler.schedule(this, TIMEOUT_MS); }
     */

}

任何人都有好主意? 如何实现连接池?

3 个答案:

答案 0 :(得分:4)

资源池(如连接池)中资源泄漏的常见原因是池的某些客户端在某些情况下无法释放资源。这是一个例子:

Resource resource = pool.getResource();
...
// do stuff
...
pool.releaseResource(resource);

如果在“do stuff”部分中抛出异常并允许传播,则会泄漏资源。以上的非泄漏版本是:

Resource resource = pool.getResource();
try {
    ... 
    // do stuff
    ...
} finally {
    pool.releaseResource(resource);
}
编辑:正如@Adamski指出的那样,没有“魔术子弹”解决方案可以解决这类问题。我建议的最好的做法是:

  1. 搜索您的代码库,找到从池中请求资源的所有位置。然后从每个点开始,检查泄漏并修复;例如基于上述模式。
  2. 创建一个测试套件,用于执行所有请求类型并针对您的服务重复运行
  3. 还有一件事。不要试图通过使用终结器来处理丢失的资源来“修复”问题。这可能会使问题似乎消失,只会在您的系统负载过重时重新出现,或者某人重要正在观看您进行演示。

答案 1 :(得分:2)

正如建议数据库池管理可能很棘手,如果您还有其他需求,例如添加连接超时或添加池管理策略(如果您有多个线程使用相同的连接,则可能就是这种情况)。

所以我建议使用开源解决方案,例如Apache DHCP或其他开源解决方案。您仍然需要正确关闭连接(如Stephen C所建议的那样),但如果您需要实现更复杂的东西,它将为您提供更大的灵活性。

答案 2 :(得分:0)

当在getConnection()中借用连接时,我会存储时间点(以及请求线程?),并在几分钟后添加一个线程将其从池中删除。这可以帮助你找到罪魁祸首:)

除非没有任何长期任务,否则不应关闭连接。我只想记录下来。