实现连接池:Java
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6595310/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implementing Connection Pooling : Java
提问by crackerplace
In one of the interviews that I faced,I was asked to implement connection pooling. So approach was this:
在我面临的一次面试中,我被要求实现连接池。所以方法是这样的:
- Create a
List
orHashMap
- Create predefined number of connections
- Add them to the collection.
- Now when the
ConnectionImpl
getConnection()
method ofConnectionPoolingImpl
class is invoked return a connection reference.
- 创建一个
List
或HashMap
- 创建预定义数量的连接
- 将它们添加到集合中。
- 现在当类的
ConnectionImpl
getConnection()
方法ConnectionPoolingImpl
被调用时返回一个连接引用。
Now when someone returns the connection (releaseConnection(ConnectionImpl O)
) how can I ensure that when the same application again tries to reuse the connection object, my implementation throws an exception?
现在,当有人返回连接 ( releaseConnection(ConnectionImpl O)
) 时,我如何确保当同一个应用程序再次尝试重用连接对象时,我的实现会抛出异常?
The same connection object might have been returned to a new application and that should be able to use it.
相同的连接对象可能已返回给新应用程序并且应该能够使用它。
My point of view would be to maintain a flag variable in another array kind of structure for each Connectionimpl
object and set that variable to a valid value. When user returns the connection object I would make that some invalid value. For every operation in my ConnectionImpl
, I will have to verify if the user had a valid flag.
我的观点是为每个Connectionimpl
对象在另一种数组结构中维护一个标志变量,并将该变量设置为有效值。当用户返回连接对象时,我会将其设为无效值。对于 my 中的每个操作ConnectionImpl
,我都必须验证用户是否具有有效标志。
What would you say to that approach?
你对这种方法有什么看法?
采纳答案by gustafc
I would not return the "real" connection object from the pool, but a wrapper which gives the poolcontrol of connection life cycle, instead of the client.
我不会从池中返回“真正的”连接对象,而是一个包装器,它提供连接生命周期的池控制,而不是客户端。
Assume you have a really simple connection, which you can read int
values from:
假设您有一个非常简单的连接,您可以从中读取int
值:
interface Connection {
int read(); // reads an int from the connection
void close(); // closes the connection
}
An implementation reading from a stream could look like this (ignoring exceptions, EOF handling, etc):
从流中读取的实现可能如下所示(忽略异常、EOF 处理等):
class StreamConnection implements Connection {
private final InputStream input;
int read(){ return input.read(); }
void close(){ input.close(); }
}
Furthermore, let's assume you have a pool for StreamConnection
s that looks like this (again, ignoring exceptions, concurrency, etc):
此外,让我们假设您有一个StreamConnection
类似这样的 s池(同样,忽略异常、并发等):
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);
}
}
The basic idea here is OK, but we can't be sure the connections are returned, and we can't be sure they aren't closed and then returned, or that you don't return a connection which came from another source altogether.
这里的基本思想是可以的,但我们不能确定连接被返回,我们不能确定它们没有关闭然后返回,或者你不返回完全来自另一个源的连接.
The solution is (of course) another layer of indirection: Make a pool which returns a wrapper Connection
which, instead of closing the underlying connection when close()
is called, returns it to the pool:
解决方案是(当然)另一层间接:创建一个返回包装器的池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();
}
}
};
}
}
This ConnectionPool
would be the only thing the client code ever sees. Assuming it is the sole owner of the StreamConnectionPool
, this approach has several advantages:
这ConnectionPool
将是客户端代码所看到的唯一内容。假设它是 的唯一所有者StreamConnectionPool
,这种方法有几个优点:
Reduced complexity and minimal impact on client code- the only difference between opening connections yourself and using the pool is that you use a factory to get hold of Connection
s (which you might already do, if you're using dependency injection). Most importantly, you always clean up your resources in the same way, i.e., by calling close()
. Just like you don't care what read
does, as long as it gives you the data you need, you don't care what close()
does, as long as it releases the resources you've claimed. You shouldn't have to think whether this connection is from a pool or not.
降低复杂性并对客户端代码的影响最小- 自己打开连接和使用池之间的唯一区别是您使用工厂来获取Connection
s (如果您使用依赖注入,您可能已经这样做了)。最重要的是,您始终以相同的方式清理资源,即通过调用close()
. 就像你不在乎做什么read
,只要它给你你需要的数据,你不在乎做什么close()
,只要它释放了你声称的资源。您不必考虑此连接是否来自池。
Protection against malicious/incorrect usage- clients can only return resources they've retrieved from the pool; they can't close the underlying connections; they can't use connections they've already returned... etc.
防止恶意/错误使用- 客户端只能返回他们从池中检索到的资源;他们无法关闭底层连接;他们不能使用他们已经返回的连接......等等。
"Guaranteed" returning of resources- thanks to our finalize
implementation, even if all references to a borrowed Connection
is lost, it is still returned to the pool (or does at least stand a chance to be returned). The connection will of course be held longer than necessary this way - possibly indefinitely, since finalization isn't guaranteed to ever run - but it's a small improvement.
“保证”资源的返还——多亏了我们的finalize
实现,即使对借用的所有引用Connection
都丢失了,它仍然会返回到池中(或者至少有机会被返回)。通过这种方式,连接当然会比必要的时间更长——可能是无限期的,因为不能保证最终确定会运行——但这是一个小小的改进。
回答by Chris Dennett
回答by Neil Coffey
OK, so if I understand correctly, your question is basically "how can we can ensure that a thread doesn't return a connection to the pool and then carry on using it?". Provided you don't pass back the "raw" Connection object to the caller, then the answer is essentially "you can put some control in somewhere if you want".
好的,所以如果我理解正确的话,您的问题基本上是“我们如何确保线程不会将连接返回到池然后继续使用它?”。如果您不将“原始” Connection 对象传回给调用者,那么答案基本上是“如果您愿意,您可以将一些控制放在某个地方”。
The actual check could involve marking each connection with which Thread "owns" it at a given moment, then making sure this is always Thread.currentThread() during any call to use the connection.
实际检查可能涉及标记 Thread 在给定时刻“拥有”它的每个连接,然后确保在任何使用该连接的调用期间这始终是 Thread.currentThread()。
It doesn't matter terribly much what object you do pass back to user of the connection to represent the connection: it could be your own wrapper implementation of Connection, or just some other wrapper object with your methods for executing queries. Whichever you use, you just need to make the abovementioned check before executing any query. Bear in mind that for security you generally shouldn't be allowing "raw" arbitrary SQL to be executed, but that all queries should be based on a well-defined PreparedStatement. So there's no particular compulsion to return an actual implementation of Connection, other than this might in some circumstances help you migrate existing code (and/or if you've decided you really do want to permit execution of arbitrary SQL).
您将什么对象传递回连接的用户以表示连接并不重要:它可以是您自己的 Connection 包装器实现,或者只是一些其他包装器对象以及您用于执行查询的方法。无论您使用哪种方式,您只需要在执行任何查询之前进行上述检查。请记住,为了安全起见,您通常不应允许执行“原始”任意 SQL,但所有查询都应基于明确定义的 PreparedStatement。因此,没有特别强制要求返回 Connection 的实际实现,除了在某些情况下这可能会帮助您迁移现有代码(和/或如果您决定确实要允许执行任意 SQL)。
In many circumstances, you could also not bother making this check. You're passing a caller a means to access your database, so it's a bit like trying to stop pilots from crashing planes into buildings by scanning them for explosives at airports: they all ready have a means of messing up your system whether or not you make the additional check.
在许多情况下,您也不必费心进行此检查。您正在向呼叫者传递一种访问您的数据库的方法,因此这有点像通过在机场扫描飞机是否有爆炸物来阻止飞行员将飞机撞入建筑物:他们都准备好了一种方法来搞乱您的系统,无论您是否进行额外检查。
回答by Premraj
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();
}
}