Реализация Пула Соединений: Java

в одном из интервью, с которым я столкнулся,меня попросили реализовать пул соединений. Так подход был такой:

  1. создать List или HashMap
  2. создать предопределенное количество соединения
  3. добавить их в коллекцию.
  4. теперь, когда ConnectionImpl getConnection() метод ConnectionPoolingImpl класс вызывается возвращает ссылку на соединение.

теперь, когда кто-то возвращает соединение (releaseConnection(ConnectionImpl O)) как я могу гарантировать, что когда то же приложение снова пытается повторно использовать объект подключения, моя реализация создает исключение?

тот же объект соединения мог быть возвращен в новое приложение, и это должно быть в состоянии использовать его.

моя точка зрения состояла бы в том, чтобы поддерживать переменную флага в другой структуре массива для каждого Connectionimpl object и установите для этой переменной допустимое значение. Когда пользователь возвращает объект подключения, я бы сделал это недопустимое значение. Для каждого операция в моем ConnectionImpl, мне нужно будет проверить, был ли у пользователя допустимый флаг.

что бы вы сказали на такой подход?

4 ответов


я бы не вернул" реальный " объект соединения из пула, но оболочку, которая дает бассейн управление жизненным циклом соединения, а не клиент.

Предположим, у вас есть действительно простое соединение, которое вы можете прочитать int значения:

interface Connection {
    int read(); // reads an int from the connection
    void close(); // closes the connection
}

чтение реализации из потока может выглядеть так (игнорирование исключений, обработка EOF и т. д.):

class StreamConnection implements Connection {
    private final InputStream input;
    int read(){ return input.read(); }
    void close(){ input.close(); }
}

кроме того, предположим, что у вас есть бассейн для StreamConnectionS, который выглядит так (опять же, игнорируя исключения, параллелизм и т. д.):

class StreamConnectionPool {
    List<StreamConnection> freeConnections = openSomeConnectionsSomehow();
    StreamConnection borrowConnection(){ 
        if (freeConnections.isEmpty()) throw new IllegalStateException("No free connections");
        return freeConnections.remove(0); 
    }
    void returnConnection(StreamConnection conn){
        freeConnections.add(conn);
    }
}

основная идея здесь в порядке, но мы не можем быть уверены, что соединения возвращаются, и мы не можем быть уверены, что они не закрыты, а затем возвращены, или что вы не возвращаете соединение, которое пришло из другого источника.

решение (конечно) еще один слой косвенности: сделайте пул, который возвращает оболочку Connection который, вместо закрытия базового соединение когда close() вызывается, возвращает его в пул:

class ConnectionPool {

    private final StreamConnectionPool streamPool = ...;

    Connection getConnection() {
        final StreamConnection realConnection = streamPool.borrowConnection();
        return new Connection(){
            private boolean closed = false;
            int read () {
                if (closed) throw new IllegalStateException("Connection closed"); 
                return realConnection.read();
            }
            void close() {
                if (!closed) {
                    closed = true;
                    streamPool.returnConnection(realConnection);
                }
            }
            protected void finalize() throws Throwable {
                try {
                    close();
                } finally {
                    super.finalize();
                }
            }
        };
    }

}

этой ConnectionPool было бы единственным, что клиентский код когда-либо видел. Предполагая, что он является единственным владельцем StreamConnectionPool этот подход имеет ряд преимуществ:

снижение сложности и минимальное влияние на клиентский код - единственная разница между открытием соединений самостоятельно и использованием пула заключается в том, что вы используете фабрику, чтобы получить Connections (что вы уже можете сделать, если вы использование инъекции зависимостей). Самое главное, вы всегда очищаете свои ресурсы одним и тем же способом, т. е., вызывая close(). Так же, как вам все равно, что read делает, пока он дает вам необходимые данные, вам все равно, что close() делает, пока он освобождает ресурсы, которые вы утверждали. Вам не нужно думать, является ли это соединение из пула или нет.

защита от вредоносного/неправильного использования - клиенты могут возвращать только ресурсы они извлекаются из пула; они не могут закрыть базовые соединения; они не могут использовать соединения, которые они уже вернули... так далее.

"гарантированное" возвращение ресурсов на finalize реализации, даже если все ссылки на заимствованный Connection теряется, он по-прежнему возвращается в пул (или, по крайней мере, имеет шанс быть возвращенным). Связь, конечно, будет длиться дольше, чем это необходимо - возможно, бесконечно, так как окончательная доработка не гарантируется, но это небольшое улучшение.


Я бы просто сказал им, что я использую JdbcConnectionPool класса (здесь), который поставляется с H2 (вы, вероятно, можете скопировать его). Винт пытается реализовать один :) это может быть вопрос с подвохом.


хорошо, поэтому, если я правильно понимаю, ваш вопрос в основном: "как мы можем гарантировать, что поток не возвращает соединение с пулом, а затем продолжает его использовать?". При условии, что вы не передаете "сырой" объект соединения вызывающему абоненту, то ответ по существу "вы можете поместить некоторый контроль где-то, если хотите".

фактическая проверка может включать маркировку каждого соединения, с которым поток "владеет" им в данный момент, а затем убедитесь, что это всегда Нитка.currentThread () во время любого вызова для использования соединения.

не имеет большого значения, какой объект вы передаете пользователю соединения для представления соединения: это может быть ваша собственная реализация оболочки соединения или просто какой-либо другой объект оболочки с вашими методами выполнения запросов. Какой бы вы ни использовали, вам просто нужно сделать вышеупомянутую проверку перед выполнением любого запроса. Имейте в виду, что для безопасности вы обычно не должны позволять " raw" произвольный SQL должен быть выполнен, но все запросы должны основываться на четко определенном PreparedStatement. Таким образом, нет особого принуждения возвращать фактическую реализацию соединения, кроме этого, в некоторых случаях это может помочь вам Перенести существующий код (и/или если вы решили, что действительно хотите разрешить выполнение произвольного SQL).

во многих случаях вы также можете не беспокоиться о том, чтобы сделать эту проверку. Вы передаете абоненту получать доступ к вашей базе данных, так что это немного похоже на попытку остановить пилотов от врезающихся самолетов в здания, сканируя их на наличие взрывчатых веществ в аэропортах: у всех них есть средство испортить вашу систему, независимо от того, делаете ли вы дополнительную проверку.


внедрение ConnectionPool

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;



/** A Connection Pool with 5 Available Connections **/
class ConnectionPool {

    private List<Connection>availableConnections = 
                                new ArrayList<Connection>();
    private List<Connection>usedConnections = new ArrayList<Connection>();
    private final int MAX_CONNECTIONS = 5;

    private String URL;
    private String USERID;
    private String PASSWORD;


    /** Initialize all 5 Connections and put them in the Pool **/
    public ConnectionPool(String Url, String UserId, String password)
            throws SQLException {
        this.URL = Url;
        this.USERID = UserId;
        this.PASSWORD = password;

        for (int count = 0; count <MAX_CONNECTIONS; count++) {
            availableConnections.add(this.createConnection());
        }

    }






/** Private function, 
    used by the Pool to create new connection internally **/

    private Connection createConnection() throws SQLException {
        return DriverManager
                .getConnection(this.URL, this.USERID, this.PASSWORD);
    }




    /** Public function, used by us to get connection from Pool **/
    public Connection getConnection() {
        if (availableConnections.size() == 0) {
            System.out.println("All connections are Used !!");
            return null;
        } else {
            Connection con = 
            availableConnections.remove(
                availableConnections.size() - 1);
            usedConnections.add(con);
            return con;
        }
    }



    /** Public function, to return connection back to the Pool **/
    public boolean releaseConnection(Connection con) {
        if (null != con) {
            usedConnections.remove(con);
            availableConnections.add(con);
            return true;
        }
        return false;
    }





    /** Utility function to check the number of Available Connections **/
    public int getFreeConnectionCount() {
        return availableConnections.size();
    }
}