2013-08-06 2 views
1

В моем приложении используется пул соединений (snaq.db.ConnectionPool). Пул соединений инициализируются как:Пул подключений DB путается - Java

String dburl = propertyUtil.getProperty("dburl"); 
String dbuserName = propertyUtil.getProperty("dbuserName"); 
String dbpassword = propertyUtil.getProperty("dbpassword"); 
String dbclass = propertyUtil.getProperty("dbclass"); 
String dbpoolName = propertyUtil.getProperty("dbpoolName"); 
int dbminPool = Integer.parseInt(propertyUtil.getProperty("dbminPool")); 
int dbmaxPool = Integer.parseInt(propertyUtil.getProperty("dbmaxPool")); 
int dbmaxSize = Integer.parseInt(propertyUtil.getProperty("dbmaxSize")); 
long dbidletimeout = Long.parseLong(propertyUtil.getProperty("dbidletimeout")); 
Class.forName(dbclass).newInstance(); 
ConnectionPool moPool = new ConnectionPool(dbpoolName, dbminPool, dbmaxPool, dbmaxSize, 
dbidletimeout, dburl, dbuserName, dbpassword); 

значений DB пула используемых являются:

dbminPool=5 
dbmaxPool=30 
dbmaxSize=30 
dbclass=org.postgresql.Driver 
dbidletimeout=25 

Моего приложение протекало соединение где-то (соединение не было выпущено) и из-за которой пул соединений получали исчерпан. На данный момент я исправил этот код.

Не следует ли закрывать соединения после периода ожидания простоя? Если это неверное предположение, есть ли способ для закрыть открытые соединения в режиме ожидания в любом случае (только через код Java)?

+0

Выполняется ли ваш код в серверной среде? –

+0

@ c.s: Да, конечно. Jboss. – Ankit

+0

Просмотрите свой код, где-то соединение не закрывается. –

ответ

3

Переменная timeout, похоже, не соответствует времени простоя соединения, но сколько времени пул может ждать, чтобы вернуть новое соединение или выбросить исключение (я посмотрел this source code, не знаю если он обновлен). Я думаю, что было бы довольно сложно отслеживать «бездействующие» соединения, потому что в этом случае означает «простоя»? Возможно, вам понадобится подключение для последующего использования. Поэтому я бы сказал, что единственный способ для пула соединений знать, что вы закончили соединение, - это позвонить close().

Если вы беспокоитесь о том, что команда разработчиков забыла позвонить close() в свой код, есть техника, которую я опишу ниже, и я использовал ее в прошлом (в моем случае мы хотели отслеживать незакрытые InputStream s, но концепция такая же).

Отказ от ответственности:

  • Я предполагаю, что соединения используются только в одном запросе и не охватывают во время последовательных запросов. В последнем случае вы не можете использовать решение ниже.
  • Ваша реализация пула соединений, похоже, уже использует аналогичные методы с теми, которые я описываю ниже (т. Е. Уже обматывает соединения), поэтому я не могу знать, будет ли это работать для вашего дела или нет. Я не тестировал код ниже, я просто использую его для описания концепции.
  • Используйте это только в своей среде разработки. В производстве вы должны быть уверены, что ваш код проверен и что он ведет себя правильно.

Сказанное выше, основная идея такова: у нас есть центральное место (пул соединений), откуда мы получаем ресурсы (соединения), и мы хотим отслеживать, освобождены ли эти ресурсы нашим кодом. Мы можем использовать веб-сайт Filter, который использует объект ThreadLocal, который отслеживает соединения, используемые во время запроса. Я назвал этот класс TrackingFilter, а объект, который отслеживает ресурсы, - это класс Tracker.

public class TrackingFilter implements Filter { 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
     Tracker.start(); 
     try { 
      chain.doFilter(request, response); 
     } finally { 
      Tracker.stop(); 
     } 
    } 

    ... 
} 

Для Tracker, чтобы иметь возможность отслеживать соединения, он должен быть уведомлен каждый раз, когда соединение приобретенную с getConnection() и каждый раз, когда соединение закрыто с close() вызова. Чтобы иметь возможность сделать это таким образом, который прозрачен для остальной части кода, нам нужно обернуть ConnectionPool и возвращенные объекты Connection. Ваш код должен вернуть новый TrackingConnectionPool вместо исходного пула (я предполагаю, что доступ к пулу подключений находится в одном месте). Этот новый пул будет завернут в свою очередь, каждый Connection, который он предоставляет, как TrackableConnection. TrackableConnection - это объект, который знает, как уведомить наш Tracker при создании и закрытии.

Когда вы позвоните Tracker.stop() в конце запроса, он сообщит о любых подключениях, для которых close() еще не был вызван. Поскольку это операция запроса, вы обнаружите только неисправные операции (т. Е. Во время функции «Создать новый продукт»), и, надеюсь, вы сможете отслеживать те запросы, которые оставляют открытые соединения и исправляют их.

Ниже вы можете найти код и комментарии для TrackingConnectionPool, TrackableConnection и категории Tracker. Методы делегатов были опущены для краткости. Надеюсь, это поможет.

Примечание. Для оберток используется автоматическая функция IDE (например, «Создание методов делегата Eclipse»), в противном случае это будет трудоемкой задачей и ошибкой.

//------------- Pool Creation 

ConnectionPool original = new ConnectionPool(String dbpoolName, ...); 
TrackingConnectionPool trackingCP = new TrackingConnectionPool(original); 

// ... or without creating the ConnectionPool yourself 
TrackingConnectionPool trackingCP = new TrackingConnectionPool(dbpoolName, ...); 

// store the reference to the trackingCP instead of the original 

//------------- TrackingConnectionPool 

public class TrackingConnectionPool extends ConnectionPool { 

    private ConnectionPool originalPool; // reference to the original pool 

    // Wrap all available ConnectionPool constructors like this 
    public TrackingConnectionPool(String dbpoolName, ...) { 
     originalPool = new ConnectionPool(dbpoolName, ...); 
    } 

    // ... or use this convenient constructor after you create a pool manually 
    public TrackingConnectionPool(ConnectionPool pool) { 
     this.originalPool = pool; 
    } 

    @Override 
    public Connection getConnection() throws SQLException { 
     Connection con = originalPool.getConnection(); 
     return new TrackableConnection(con); // wrap the connections with our own wrapper 
    } 
    @Override 
    public Connection getConnection(long timeout) throws SQLException { 
     Connection con = originalPool.getConnection(timeout); 
     return new TrackableConnection(con); // wrap the connections with our own wrapper 
    } 

    // for all the rest public methods of ConnectionPool and its parent just delegate to the original 
    @Override 
    public void setCaching(boolean b) { 
     originalPool.setCaching(b); 
    } 
    ... 
} 

//------------- TrackableConnection 

public class TrackableConnection implements Connection, Tracker.Trackable { 

    private Connection originalConnection; 
    private boolean released = false; 

    public TrackableConnection(Connection con) { 
     this.originalConnection = con; 
     Tracker.resourceAquired(this); // notify tracker that this resource is aquired 
    } 

    // Trackable interface 

    @Override 
    public boolean isReleased() { 
     return this.released; 
    } 

    // Note: this method will be called by Tracker class (if needed). Do not invoke manually 
    @Override 
    public void release() { 
     if (!released) { 
      try { 
       // attempt to close the connection 
       originalConnection.close(); 
       this.released = true; 
      } catch(SQLException e) { 
       throw new RuntimeException(e); 
      } 
     } 
    } 

    // Connection interface 

    @Override 
    public void close() throws SQLException { 
     originalConnection.close(); 
     this.released = true; 

     Tracker.resourceReleased(this); // notify tracker that this resource is "released" 
    } 

    // rest of the methods just delegate to the original connection 

    @Override 
    public Statement createStatement() throws SQLException { 
     return originalConnection.createStatement(); 
    } 
    .... 
} 

//------------- Tracker 

public class Tracker { 

    // Create a single object per thread 
    private static final ThreadLocal<Tracker> _tracker = new ThreadLocal<Tracker>() { 
      @Override 
      protected Tracker initialValue() { 
       return new Tracker(); 
      }; 
    }; 

    public interface Trackable { 
     boolean isReleased(); 
     void release(); 
    } 

    // Stores all the resources that are used during the thread. 
    // When a resource is used a call should be made to resourceAquired() 
    // Similarly when we are done with the resource a call should be made to resourceReleased() 
    private Map<Trackable, Trackable> monitoredResources = new HashMap<Trackable, Trackable>(); 

    // Call this at the start of each thread. It is important to clear the map 
    // because you can't know if the server reuses this thread 
    public static void start() { 
     Tracker monitor = _tracker.get(); 
     monitor.monitoredResources.clear(); 
    } 

    // Call this at the end of each thread. If all resources have been released 
    // the map should be empty. If it isn't then someone, somewhere forgot to release the resource 
    // A warning is issued and the resource is released. 
    public static void stop() { 
     Tracker monitor = _tracker.get(); 
     if (!monitor.monitoredResources.isEmpty()) { 
      // there are resources that have not been released. Issue a warning and release each one of them 
      for (Iterator<Trackable> it = monitor.monitoredResources.keySet().iterator(); it.hasNext();) { 
       Trackable resource = it.next(); 

       if (!resource.isReleased()) { 
        System.out.println("WARNING: resource " + resource + " has not been released. Releasing it now."); 
        resource.release(); 
       } else { 
        System.out.println("Trackable " + resource 
          + " is released but is still under monitoring. Perhaps you forgot to call resourceReleased()?"); 
       } 
      } 
      monitor.monitoredResources.clear(); 
     } 
    } 

    // Call this when a new resource is acquired i.e. you a get a connection from the pool 
    public static void resourceAquired(Trackable resource) { 
     Tracker monitor = _tracker.get(); 
     monitor.monitoredResources.put(resource, resource); 
    } 

    // Call this when the resource is released 
    public static void resourceReleased(Trackable resource) { 
     Tracker monitor = _tracker.get(); 
     monitor.monitoredResources.remove(resource); 
    } 
} 
3

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

try { 
    conn = moPool.getConnection(timeout); 
    if (conn != null) 
    // do something 
} catch (Exception e) { 
    // deal with me 
} finally { 
    try { 
    conn.close(); 
    } catch (Exception e) { 
    // maybe deal with me 
    } 
} 
  • E
+0

Я согласен с тем, что лучшим решением является закрытие соединения, и я исправил код для этого. Но мой вопрос касается значения idletimeout, которое пул соединений принимает в конструкторе. Кроме того, поскольку код все еще находится в разработке, могут быть случаи, когда соединение не выпущено (я согласен, что это не очень хорошая практика). Таким образом, должен быть способ выйти из этой ситуации прагматично в одном месте (метод getConnection), который скажет мне об отключении связи. – Ankit

1

Весь смысл пулы соединений должен позволить бассейн обрабатывать все такие вещи для вас.

  • Имея код для closing open idle connections of java pool, не поможет в вашем случае.
  • Подумайте о подключении пула, поддерживающего MAP для IDLE или IN-USE соединений.
  • IN-USE: Если объект подключения ссылается на приложение, он помещается в in-use-map пулом.
  • IDLE: Если объект связи не ссылается на заявку/или closed, он помещается в idle-map пулом.
  • Ваш бассейн исчерпан, потому что вы не закрывали соединения. Не закрытие соединений привело все idle соединений, которые будут помещены в in-use-map.
  • Поскольку idle-pool не имеет записи, пул вынужден создать больше из них.
  • Таким образом, все ваши соединения отмечены как IN-USE.
  • Ваш пул не имеет open-idle-connections, который вы можете закрыть по коду.
  • Пул не в состоянии закрыть любое соединение, даже если тайм-аут происходит, потому что ничего не работает.

Вы приложили максимум усилий, если исправили утечку соединения из своего кода.

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

0

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

Некоторые пулов соединений также тайм-аут настройки для того, как долго соединение разрешено быть в использовании (например, ДБХП имеет removeAbandonedTimeout, c3p0 имеет unreturnedConnectionTimeout), и, если таковые включены и тайм-аут истек, они будут принудительно отозваны пользователь и либо вернулся в пул, либо действительно закрыт.

0

log4jdbc может использоваться для устранения проблем с утечкой при помощи с помощью jdbc.connection регистратора.

Этот метод не требует модификации кода.

Смежные вопросы