实现连接池:Java
在我面临的一次面试中,我被要求实现连接池。 所以方法是这样的:
- 创建一个
List
或HashMap
- 创建预定义数量的连接
- 将它们添加到集合中。
- 现在,当调用
ConnectionPoolingImpl
类的ConnectionImpl
getConnection()
方法时,会返回一个连接引用。
现在,当有人返回连接 (releaseConnection(ConnectionImpl O)
) 时,我如何确保当同一应用程序再次尝试重用连接对象时,我的实现会引发异常?
相同的连接对象可能已返回到新应用程序,并且应该能够使用它。
我的观点是在另一种数组类型的结构中为每个 Connectionimpl 对象维护一个标志变量,并将该变量设置为有效值。当用户返回连接对象时,我会将其设置为一些无效值。对于 ConnectionImpl
中的每个操作,我都必须验证用户是否具有有效的标志。
您对这种做法有何看法?
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.
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?
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.
What would you say to that approach?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我不会从池中返回“真正的”连接对象,而是返回一个包装器,它为池提供连接生命周期的控制权,而不是客户端。
假设您有一个非常简单的连接,您可以从中读取
int
值:从流读取的实现可能如下所示(忽略异常、EOF 处理等):
此外,我们假设您有一个StreamConnection 的池看起来像这样(同样,忽略异常、并发等):
这里的基本思想是好的,但是我们不能确定连接是否被返回,而且我们也不能务必它们不会关闭然后返回,或者您不会完全返回来自另一个源的连接。
解决方案(当然)是另一层间接:创建一个返回包装器
Connection
的池,该包装器在调用close()
时不会关闭底层连接,而是返回它到池中:这个
ConnectionPool
将是客户端代码所看到的唯一东西。假设它是 StreamConnectionPool 的唯一所有者,此方法有几个优点:降低复杂性并对客户端代码影响最小 - 这是自己打开连接和使用池之间的唯一区别是您使用工厂来获取连接(如果您使用依赖注入,您可能已经这样做了)。最重要的是,您始终以相同的方式清理资源,即通过调用
close()
。就像你不关心read
做什么,只要它给你需要的数据,你也不关心close()
做什么,只要它会释放您已声明的资源。您不必考虑此连接是否来自池。防止恶意/不正确的使用 - 客户端只能返回从池中检索到的资源;他们无法关闭底层连接;他们不能使用已经返回的连接...等
“保证”返回资源 - 得益于我们的
finalize
实现,即使所有引用都是借用的连接
丢失,它仍然返回到池中(或者至少有机会返回)。通过这种方式,连接的保持时间当然会比必要的时间长——可能是无限期的,因为最终确定不能保证永远运行——但这只是一个小小的改进。I would not return the "real" connection object from the pool, but a wrapper which gives the pool control of connection life cycle, instead of the client.
Assume you have a really simple connection, which you can read
int
values from:An implementation reading from a stream could look like this (ignoring exceptions, EOF handling, etc):
Furthermore, let's assume you have a pool for
StreamConnection
s that looks like this (again, ignoring exceptions, concurrency, etc):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 whenclose()
is called, returns it to the pool:This
ConnectionPool
would be the only thing the client code ever sees. Assuming it is the sole owner of theStreamConnectionPool
, this approach has several advantages: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 callingclose()
. Just like you don't care whatread
does, as long as it gives you the data you need, you don't care whatclose()
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.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 borrowedConnection
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.我只是告诉他们我会使用
JdbcConnectionPool
类 (此处)是 H2 附带的(您可以将其复制出来)。螺丝试图实现一个:)这可能是一个棘手的问题。I'd just tell them I'd use the
JdbcConnectionPool
class (here) that comes with H2 (you can probably copy it out). Screw trying to implement one :) It could be a trick question.连接池实现
ConnectionPool implemenation
好的,所以如果我理解正确的话,你的问题基本上是“我们如何确保线程不会将连接返回到池然后继续使用它?”。如果您不将“原始”Connection 对象传递回调用者,那么答案本质上是“如果您愿意,您可以在某个地方放置一些控制”。
实际的检查可能涉及标记线程在给定时刻“拥有”它的每个连接,然后确保在使用该连接的任何调用期间始终是 Thread.currentThread()。
您将什么对象传递回连接的用户来表示连接并不重要:它可以是您自己的 Connection 包装器实现,或者只是带有用于执行查询的方法的其他包装器对象。无论您使用哪种,您只需要在执行任何查询之前进行上述检查即可。请记住,为了安全起见,您通常不应允许执行“原始”任意 SQL,但所有查询都应基于定义良好的PreparedStatement。因此,没有特别强制返回 Connection 的实际实现,除了在某些情况下这可能会帮助您迁移现有代码(和/或如果您确定确实希望允许执行任意 SQL)。
在许多情况下,您也不必费心进行此项检查。你正在向呼叫者传递一种访问数据库的方法,所以这有点像试图通过在机场扫描飞机是否有爆炸物来阻止飞行员将飞机撞向建筑物:他们都准备好了一种方法来搞乱你的系统,无论你是否进行附加检查。
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".
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.
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).
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.