如何对连接做池化
1. 为什么要对连接做池化?
每次创建一个连接Connection的时候,都是重新创建,使用完之后又丢掉了,如果频繁地有创建Connection的请求,那么创建Connection的开销是非常大的,如下图1所示。而这一部分的性能开销,其实可以通过回收并且再分配Connection的方式来减少,而这一步就是所谓的池化Pooling。
2. 实现池化思考过程
我们先来思考一下,要实现池化的话,需要哪些必要的东西或者组件呢?首先最容易想到的一点就是,肯定需要一个池子,池子是一个必须存在客观的对象,而池子的作用就是用来分配和回收连接的,因此我们现在的脑海中应该有一幅类似下面这样的概念图。现在看来,毫无疑问这里面应该要有两种对象,一个是池子ConnectionPool,一个是连接Connection,下面都分别称作ConnectionPool和Connection。
那么现在问题的关键在于,ConnectionPool怎么创建和回收Connection呢?创建和回收分别和create、close这两个操作相关,create是ConnectionPool发出的,而close则应该是由Connection发出。我们接着继续想这个问题,肯定是先有create再有close,所以我们先考虑这个操作应该怎么创建Connection,ConnectionPool本身只是负责分配和回收Connection,所以这里真正创建Connection的步骤,应该要借助ConnectionFactory来实现。所以我们现在意识到了一点,ConnectionPool里面必然要包含ConnectionFactory这样的一个对象,即使不存在ConnectionFactory,也必然会在概念上存在这样的一个角色。
继续往下想,ConnectionPool既然要做到回收并且再利用Connection,那它肯定必须要有存储Connection的容器,因此应该要有一个队列List来存放可再利用的Connection。现在的问题是,什么时候往这个List里面add Connection,以及什么时候从List里面get Connection呢?1)add Connection应该是Connection close的时候,因为要回收Connection,所以不能让Connection真的close掉,而应该在它close的时候将它放入到List里面;2)对应的get Connection应该是在ConnectionPool进行create Connection操作的时候发生,ConnectionPool应该先检查List里面有没有空闲的Connection,如果有就拿出来分配,如果没有的话那就利用ConnectionFactory去真正地创建一个Connection再分配。
好了,到现在整个模型的逻辑就基本清晰了,但还有个问题没解决,将Connection add到List这一步是在Connection close的时候做的,但Connection的close操作有它自己的逻辑,我们没法去改变这个已有的close操作的逻辑,比如Connection的close它就是关闭连接销毁自身,那我们没办法让它在close的时候add到List里面啊,那怎么办呢?因此我们发现这个地方要有一个Connection的代理对象,所以应该要有一个PooledConnection的代理对象对Connection做代理封装,而这个PooledConnection要覆盖Connection的close方法。而ConnectionPool在create的时候,返回的应该是封装过的PooledConnection对象而不是Connection。那什么时候封装Connection成为PooledConnection呢?显然,应该在ConnectionFactory创建Connection之后做这一步。
到现在为止,我们的脑海中的模型在图2的基础上进一步峰峰了,它应该是这样的:
3. 池化的实现范例
基于上面的思路,一个完整的池化实现范例的UML类图如下图所示,此处稍有几点不同需要注意:
1)ConnectionPool里面单独分离出了一个PoolState对象来表达这个池子的状态,状态里面天然就包括目前空闲和正在使用的连接。PoolState有两个List,一个用于存放空闲的PooledConnection,另外一个用来存放已经分配出去的、正在使用中的PooledConnection。通常来说一个List也可以实现池化的功能,但这里之所以用了两个List,不仅可以管理空闲的PooledConnection,还可以对已经分配出去的使用中的PooledConnection做管理,同时可以对这些使用中的PooledConnection做统计,统计一些有用的信息;
- PoolState可以记录一些统计信息,比如总共有多少连接请求、平均的连接使用时间等等;
3)PooledConnection对Connection做了代理,增加了一些关于Connection的状态信息,比如最后使用的时间戳lastUsedTimestamp,每次调用方法的时候都去更新这个lastUsedTimestamp。记录这个lastUsedTimestamp有什么好处呢?当ConnectionPool要分配PooledConnection却没有空闲的PooledConnection时,可以从使用中的PooledConnection里面看看有没有超时的,如果有那就直接回收它用它来分配,那判断超时这一步就可以基于lastUsedTimestamp来做了。
对应的代码可以在这个github地址中获取:https://github.com/JiamingMai/pooling-demo 代码在MyBatis源码当中的数据源模块的基础上改造,有兴趣的同学可以参考MyBatis源码当中的PooledDataSource这个类。
以上是 如何对连接做池化 的全部内容, 来源链接: utcz.com/z/516944.html