Переменная 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);
}
}
Выполняется ли ваш код в серверной среде? –
@ c.s: Да, конечно. Jboss. – Ankit
Просмотрите свой код, где-то соединение не закрывается. –