如何编写出高效的数据库连接池(附带完整代码C#和Java实现)

6 篇文章 0 订阅
3 篇文章 0 订阅

声明:此文引用了 “倚天万里须长剑”的博客,十分受益,本转载仅供自己学习研究用,谢谢原创

转载网址: http://blog.163.com/yangjun1988422@126/blog/static/4741291720083224952187/


引言

一般的数据库应用程序大致都遵循下面的步骤:

  而本文则着重讲解上面第4步骤.在着一步骤中我们经常是,打开数据库连接操作数据库,最后关闭数据库.
  在服务器端程序设计上与数据库的操作显得十分重要,因为你要处理的数据操作十分巨大.如果频繁创建数据库连接频繁关闭数据库连接则会引起效率低下甚至引发程序崩溃.
  也许我们可以有另一种操作数据库的形式,我们可以在程序运行时打开一个数据库连接,让这个连接永久存在直到程序'死亡',那么这样做也有不安全隐患,我们知道一个对象存在时间越长或被使用次数越多则它表现的越不稳定,着不稳定因素是由于对象内部可能存在的潜在设计问题产生,对于数据库连接对象道理也一样.我们不能保证一个Connection对象里面能一点问题不存在.所以我们也不敢长时间将它长时间占用内存.
  既然有这么多的问题由此我们需要一个能帮我们维护数据库连接的东西-它就是连接池,网上有很多的连接池例子,但是多数都是简单的例子,或者介绍比较复杂的连接池原理,没有一个比较完整介绍和实现连接池的例子.这里就介绍你如何自己制作一个连接池.
  对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决我们的问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图。

数据库连接池(Connection Pool)的工作原理

如何编写出高效的数据库连接池(附带完整代码C和Java实现) - 紫龍劍 - 倚天万里须长剑

 连接池关键问题分析

  1、并发问题

  为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考,相关文献。

  2、事务处理

  我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
  我们知道当2个线程公用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。

  3、连接池的分配与释放

  连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
  对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。

  连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。
  如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。

关键议题

引用记数

  在分配、释放策略对于有效复用连接非常重要,我们采用的方法也是采用了一个很有名的设计模式:Reference Counting(引用记数)。该模式在复用资源方面用的非常广泛,我们把该方法运用到对于连接的分配释放上。每一个数据库连接,保留一个引用记数,用来记录该连接的使用者的个数。具体的实现上,我们对Connection类进行进一步包装来实现引用记数。被包装的Connection类我们提供2个方法来实现引用记数的操作,一个是Repeat(被分配出去)Remove(被释放回来);然后利用RepeatNow属性来确定当前被引用多少,具体是哪个用户引用了该连接将在连接池中登记;最后提供IsRepeat属性来确定该连接是否可以使用引用记数技术。一旦一个连接被分配出去,那么就会对该连接的申请者进行登记,并且增加引用记数,当被释放回来时候就删除他已经登记的信息,同时减少一次引用记数。

  这样做有一个很大的好处,使得我们可以高效的使用连接,因为一旦所有连接都被分配出去,我们就可以根据相应的策略从使用池中挑选出一个已经正在使用的连接用来复用,而不是随意拿出一个连接去复用。策略可以根据需要去选择,我们有4策略可是使用:

  使用空闲的实际连接分配连接资源,并且在该资源释放回之前,该资源在连接池中将不能将其引用分配给其他申请者。如果连接池中所有实际连接资源都已经分配出去,那么即使连接池可以在分配引用资源在该模式下连接迟将不会分配连接资源,连接池会产生一个异常,标志连接池资源耗尽。
  例:假如一个实际连接可以被分配5次,那么使用该模式申请连接的话您将损失4个可分配的连接,只将得到一个连接资源。 直至该资源被释放回连接池,连接池才继续分配它剩余的4次机会。 
  当你在使用连接时可能应用到事务时,可以使用该模式的连接,以确定在事务进行期间您可以对该连接具有独享权限,以避免各个数据库操作访问的干扰。
2.ConnLevel_High 优先级-高
  使用空闲的实际连接分配连接资源,并且在该资源释放回之前,该资源在连接池中将可能将其引用分配给其他申请者。*注意:此级别不保证在分配该资源后,仍然保持独立占有连接资源,若想独立占有资源请使用ReadOnely, 因为当连接池达到某一时机时该资源将被重复分配(引用记数)然而这个时机是不可预测的。如果您申请的连接会用于事务处理您可以使用ConnLevel_ReadOnly级别。
3.ConnLevel_None 优先级-中
  适当应用引用记数技术分配连接资源。
在该模式下,连接池内部会按照实际连接已经使用次数排序(多->少),然后在结果中选取 1/3 位置的连接资源返回。与优先级-高相同该模式也不具备保持独立占有连接资源的特性。如果您申请的连接会用于事务处理您可以使用ConnLevel_ReadOnly级别。
尽可能使用引用记数技术分配连接。在该模式下,连接池内部会按照实际连接已经使用次数排序(多->少),然后在结果中选取被使用最多的返回。该模式适合处理较为不重要的连接资源请求。与优先级-高相同该模式也不具备保持独立占有连接资源的特性。如果您申请的连接会用于事务处理您可以使用ConnLevel_ReadOnly级别。

以上4条策略选自datebasepool_SDK(datebasepool是本文所开发的最终产物)

如何实现事务处理

   前面谈到的都是关于使用数据库连接进行普通的数据库访问。对于事务处理,情况就变得比较复杂。因为事务本身要求原子性的保证,此时就要求对于数据库的操作符合"All-All-Nothing"原则,即要么全部完成,要么什么都不做。如果简单的采用上述的连接复用的策略,就会发生问题,因为没有办法控制属于同一个事务的多个数据库操作方法的动作,可能这些数据库操作是在多个连接上进行的,并且这些连接可能被其他非事务方法复用。
  Connection本身具有提供了对于事务的支持,具体实现方法请参看Connection的msdn,显式的调用commit或者rollback方法来实现。但是要安全、高效的进行Connection进行复用,就必须提供相应的事务支持机制。我们采用的方法是:用户以ConnLevel_ReadOnly模式申请一个连接之后该连接就由该申请者独自享有该连接,具体事务操作由用户自行设计编写,连接池只提供用户独自占有。

 管理连接池

  在上文中我们说过这个连接池内部连接管理使用的是独立的线程来工作(threadCreate和threadCheck)threadCreate线程负责创建连接 ,threadCheck线程负责检查每个连接是否达到自己的寿命,标志连接寿命的条件是被引用的次数超过它最大被引用次数,或者达到最大生存时间。这些参数都由ConnStruct类管理,ConnStruct类是包装连接的类,下面定义的就是连接池使用到的属性变量(C#代码)

//属性
privateint _realFormPool;//连接池中存在的实际连接数(包含失效的连接)
privateint _potentRealFormPool;//连接池中存在的实际连接数(有效的实际连接)
privateint _spareRealFormPool;//空闲的实际连接
privateint _useRealFormPool;//已分配的实际连接
privateint _readOnlyFormPool;//连接池已经分配多少只读连接
privateint _useFormPool;//已经分配出去的连接数
privateint _spareFormPool;//目前可以提供的连接数
privateint _maxConnection;//最大连接数,最大可以创建的连接数目
privateint _minConnection;//最小连接数
privateint _seepConnection;//每次创建连接的连接数
privateint _keepRealConnection;//保留的实际空闲连接,以攻可能出现的ReadOnly使用,当空闲连接不足该数值时,连接池将创建seepConnection个连接
privateint _exist =20;//每个连接生存期限 20分钟
privateint _maxRepeatDegree =5;//可以被重复使用次数(引用记数),当连接被重复分配该值所表示的次数时,该连接将不能被分配出去
//当连接池的连接被分配尽时,连接池会在已经分配出去的连接中,重复分配连接(引用记数)。来缓解连接池压力
private DateTime _startTime;//服务启动时间
privatestring _connString =null;//连接字符串
private ConnTypeEnum _connType;//连接池连接类型
private PoolState _ps;内部对象
private ArrayList al_All =new ArrayList();//实际连接
private Hashtable hs_UseConn =new Hashtable();//正在使用的连接
private System.Timers.Timer time;//监视器记时器
private Thread threadCreate;//创建线程
privatebool isThreadCheckRun =false;

Java版本

//属性
privateint _RealFormPool; //连接池中存在的实际连接数(包含失效的连接)
privateint _PotentRealFormPool; //连接池中存在的实际连接数(有效的实际连接)
privateint _SpareRealFormPool; //空闲的实际连接
privateint _UseRealFormPool; //已分配的实际连接
privateint _ReadOnlyFormPool; //连接池已经分配多少只读连接
privateint _UseFormPool; //已经分配出去的连接数
privateint _SpareFormPool; //目前可以提供的连接数
privateint _MaxConnection; //最大连接数,最大可以创建的连接数目
privateint _MinConnection; //最小连接数
privateint _SeepConnection; //每次创建连接的连接数
privateint _KeepRealConnection; //保留的实际空闲连接,以攻可能出现的ReadOnly使用,当空闲连接不足该数值时,连接池将创建seepConnection个连接
privateint _Exist =20; //每个连接生存期限 20分钟
private String _userID=""private String _password=""//可以被重复使用次数(引用记数),当连接被重复分配该值所表示的次数时,该连接将不能被分配出去
//当连接池的连接被分配尽时,连接池会在已经分配出去的连接中,重复分配连接(引用记数)。来缓解连接池压力
privateint _MaxRepeatDegree =5private Date _StartTime; //服务启动时间
private String _ConnString =null; //连接字符串
private String _DriveString =null; //驱动字符串
privateint _PoolState; 内部对象
private ArrayList al_All =new ArrayList(); //实际连接
private Hashtable hs_UseConn =new Hashtable(); //正在使用的连接
privateprivate CheckThreadProcess threadCheck;

  当用户调用连接池的StartService方法时,在StartService方法中会通知threadCreate线程创建静态连接,然后将这些静态连接加入到List,同时启动threadCheck线程,threadCheck线程负责检测List中的最小空闲连接是否少于连接池配置的最少空闲连接数,当条件为真时threadCheck线程会负责再次唤醒threadCreate线程同时给threadCreate线程传递这次要创建的连接个数。
  对于threadCreate线程有2种工作模式,模式0为初始化创建模式,该模式会创建连接迟池配置的最小连接数目;模式1即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接时的工作模式。
  申请连接,当用户申请连接时必须指定一个发起者和一个申请优先级别,优先级由ConnLevel_*系列指定。一旦用户从连接池中申请到一个连接时就将申请到的连接引用和申请者,同时加入到HashTable来注册到连接池。
  释放连接,将注册的用户删除。  

Java版本的构造方法

public ConnectionPool() "", "", 200, 30, 10, 5
public ConnectionPool(String connectionString, String driveString) {
        InitConnectionPool(connectionString, driveString, 200, 30, 10, 5
public ConnectionPool(String connectionString, String driveString, int maxConnection, int minConnection) {
        InitConnectionPool(connectionString, driveString, maxConnection, minConnection, 10, 5
public ConnectionPool(String connectionString, String driveString, int maxConnection, int minConnection, int seepConnection, int keepRealConnection) {
        InitConnectionPool(connectionString, driveString, maxConnection, minConnection, seepConnection, keepRealConnection);
    }

C#版本的构造方法

public ConnectionPool(string{ InitConnectionPool(connectionString, ConnTypeEnum.Odbc, 200, 30, 10, 5, 5); }
public ConnectionPool(string connectionString, ConnTypeEnum cte)
{ InitConnectionPool(connectionString, cte, 200, 30, 10, 5, 5); }
public ConnectionPool(string connectionString, ConnTypeEnum cte, int maxConnection, int{ InitConnectionPool(connectionString, cte, maxConnection, minConnection, 10, 5, 5); }
public ConnectionPool(string connectionString, ConnTypeEnum cte, int maxConnection, int minConnection, int seepConnection, int{ InitConnectionPool(connectionString, cte, maxConnection, minConnection, seepConnection, keepConnection, 5); }
public ConnectionPool(string connectionString, ConnTypeEnum cte, int maxConnection, int minConnection, int seepConnection, int keepConnection, int{ InitConnectionPool(connectionString, cte, maxConnection, minConnection, seepConnection, keepConnection, keepRealConnection); }

上面2个版本的构造方法都调用InitConnectionPool方法,下面是InitConnectionPool方法实现(C#)

///<summary>//</summary>
protectedvoid InitConnectionPool(string connectionString, ConnTypeEnum cte, int maxConnection, int minConnection, int seepConnection, int keepConnection, intif (cte ==thrownew ConnTypeExecption();//参数不能是None
            _ps =this._connString =this._connType =this._minConnection =this._seepConnection =this._keepRealConnection =this._maxConnection =this.time =new System.Timers.Timer(500thisthis.time.Elapsed +=new System.Timers.ElapsedEventHandler(time_Elapsed);
this.threadCreate =new Thread(new ThreadStart(createThreadProcess));
        }

   在InitConnectionPool方法中我们定义了连接池要连接的数据库类型ConnTypeEnum cte,和连接字符串string connectionString等类型,在方法最后注册了Timer类的Elapsed 事件,在C#版中Timer类就充当threadCheck线程,而Elapsed事件就是线程threadCheck的执行方法体。
  由于刚刚初始化连接池对象故threadCheck线程不需要运行所以在InitConnectionPool方法中将Timer关闭。下面是java实现:

privatevoid InitConnectionPool(String connectionString, String driveString, int maxConnection, int minConnection, int seepConnection, int keepRealConnection) this._PoolState =this._ConnString =this._DriveString =this._MinConnection =this._SeepConnection =this._KeepRealConnection =this._MaxConnection =this.threadCheck =newthis.threadCheck.setDaemon(truethis.threadCreate =newthis.threadCreate.setDaemon(truethiswhile(threadCheck.getState()!=Thread.State.WAITING){}

  由于Java中的Timer类需要AWT 的GUI线程组建支持,总不能在我们的连接池中开辟一个Form出来把,因此在Java版本中使用线程来实现Timer,在Java版本中threadCheck一开始就被启动,然后又等待threadCheck线程自己被阻塞又是为什么呢,这样做是为了实现C#版本中stop方法.(由于个人原因这个连接池最初是由C#.Net2.0开发而成的故而在Java中可能存在许多设计不合理)。
  由上面代码可以看出一旦连接池被创建出来以后就已经运行了一个threadCheck线程(对于Java版),而这个线程将立刻进入WAITING等待状态。

///<summary>//</summary>
publicvoidlock (this=0;//工作模式0
                createThreadProcessRun =true;
                createThreadProcessTemp =if (_ps == PoolState.UnInitialize)
                    threadCreate.Start();
elseif (_ps == PoolState.Stop)
                    threadCreate.Interrupt();
elsethrownew PoolNotStopException();//服务已经运行或者未完全结束
                time.Start();
            }

  createThreadMode 是设置threadCreate的工作模式为0创建模式,前面我们说过threadCreate有2种工作模式,如果忘记了就回头去看看把。
  createThreadProcessTemp是threadCreate工作需要的参数变量threadCreate会在0或者1这两种工作模式下用到这个临时变量该变量作用就是告诉threadCreate要创建多少个连接,下面的一组if .... else 是为了将threadCreate线程运行起来 _ps是表示当前连接池状态,下面是_ps的定义,在Java版本中PoolState只是一组public static final int故在这里不在将源码放出。
  在StartServices最后我们启动了time也就是threadCheck线程。

// 连接池状态
publicenum// 刚刚创建的对象,表示该对象未被调用过StartSeivice方法。
        Initialize,// 初始化中,该状态下服务正在按照参数初始化连接池。
        Run,//运行中
        Stop//停止状态
    }

PoolState定义

Java版本

/**@throws*/publicvoid StartServices() throws PoolNotStopException synchronized (this) {
            threadCreate.createThreadMode =0; //工作模式0
            threadCreate.createThreadProcessRun =true;
            threadCreate.createThreadProcessTemp =if (_PoolState == PoolState_UnInitialize)
                threadCreate.start();
elseif (_PoolState == PoolState_Stop)
                threadCreate.interrupt();
elsethrownew PoolNotStopException(); //服务已经运行或者未完全结束
            threadCheck.StartTimer();
            threadCheck.interrupt();//开始运行
        }
///<summary>//</summary>
publicvoid///<summary>//<param name="needs">是否必须退出;如果指定为false与StartServices()功能相同,如果指定为true。将未收回的连接资源关闭,这将是危险的。认为可能你的程序正在使用此资源。</param>///</summary>
publicvoid StopServices(boollock (thisif (_ps ==lockif (needs ==true)//必须退出
                            hs_UseConn.Clear();
elseif (hs_UseConn.Count !=0thrownew ResCallBackException();//连接池资源未全部回收
                    }while (isThreadCheckRun) { }//等待timer事件结束
                    createThreadProcessRun =falsewhile (threadCreate.ThreadState != ThreadState.WaitSleepJoin) { }//等待可能存在的创建线程结束
lockfor (int i =0; i < al_All.Count; i++)
                            ((ConnStruct)al_All[i]).Dispose();
                        al_All.Clear();
                    }=elsethrownew PoolNotRunException();//服务未启动
            }//更新属性
        }

  在这里我们使用了2层锁第一层锁我们是为了保证同一时间只能有一个线程调用这个方法,而lock (hs_UseConn)则是为了保护hs_UseConn的唯一访问hs_UseConn存放的就是申请连接时注册信息,needs是将未收回的连接资源也关闭,这将是危险的。认为可能你的程序正在使用此资源。
       if (hs_UseConn.Count !=0thrownew ResCallBackException();//连接池资源未全部回收。
  这段代码是表示如果用户试图在连接没有全部释放回来时候将会引发异常,同时执行该方法的必要条件是连接池必须处在PoolState_Run状态。
  time.Stop();是停止检测线程的运行,while (isThreadCheckRun) { }那为什么要使用这个空循环等待timer的结束呢?原因是Timer类的运行基理,在C#下Timer触发的事件是不同于VB的,在.Net里无论Timer事件处理过程的执行时间是否超过它触发时间间隔在Timer里总会在指定时间被调用,也就是说如果触发间隔却是100毫秒,然而我们的处理过程用了500毫秒来运行代码。这样的话下一个事件的触发将在我们这个事件还没有结束时又进行了,如此一来起不是灾难的!
  对于接下来我们还要等待可能存在的threadCreate线程结束,不过在等待它结束时先给他发送一个暂停信号,这个信号由createThreadProcessRun 变量传递进去。也许有人回问为什么不使用join来阻止threadCreate的运行,原因是这样的,假如threadCreate打算创建minConnection个连接,然而恰恰在创建到minConnection - n 个时候我停止也连接池,如果在这里使用join的话我们将不确定下次恢复运行时候究竟要创建多少个连接,最好的解决办法就是叫线程自己找一个安全的地方把自己join。
  在下面的代码就是把每一个连接分别销毁掉并且在连接池中将它删除掉。有人说,那你只是删除了你内部的引用,的确我们只删除了内部的引用,因为销毁只是形式的销毁在.net上我们不能控制内存去销毁一个对象,只能叫GC帮忙来清理它。在程序段的最后我们使用私有的UpdateAttribute方法来更新属性字段。

 下面是Java版本的代码

/**@throws@throws*/publicvoid StopServices() throws ResCallBackException, PoolNotRunException /**@param needs boolean 是否必须退出;如果指定为false与StartServices()功能相同,如果指定为true。将未收回的连接资源关闭,这将是危险的。认为可能你的程序正在使用此资源。
     * @throws@throws*/publicvoid StopServices(boolean needs) throws PoolNotRunException, ResCallBackException synchronized (this) if (_PoolState == PoolState_Run) synchronized (hs_UseConn) if (needs ==true) //必须退出
                        hs_UseConn.clear();
elseif (hs_UseConn.size() !=0thrownew ResCallBackException(); //连接池资源未全部回收
                }while (threadCreate.getState()!=Thread.State.WAITING) { }//等待threadCreate事件结束
                threadCreate.createThreadProcessRun =falsewhile (threadCreate.getState() != Thread.State.WAITING) {}//等待可能存在的创建线程结束synchronized (al_All) for (int i =0; i < al_All.size(); i++)
                        ((ConnStruct) al_All.get(i)).Dispose();
                    al_All.clear();
                }=elsethrownew PoolNotRunException(); //服务未启动
        }//更新属性
    }
///<summary>//</summary>///<param name="gui">发起者</param>///<returns>返回申请到的连接</returns>
public DbConnection GetConnectionFormPool(object{ return GetConnectionFormPool(key, ConnLevel.None); }///<summary>//</summary>///<param name="key">申请者</param>///<param name="cl">申请的连接级别</param>///<returns>返回申请到的连接</returns>
public DbConnection GetConnectionFormPool(objectlock (thisif (_ps !=thrownew StateException();//服务状态错误
if (hs_UseConn.Count ==thrownew PoolFullException();//连接池已经饱和,不能提供连接
ifthrownew KeyExecption();//一个key对象只能申请一个连接
if (cl ==return GetConnectionFormPool_ReadOnly(key);//ReadOnly级别
elseif (cl ==return GetConnectionFormPool_High(key);//High级别
elseif (cl ==return GetConnectionFormPool_None(key);//None级别
elsereturn GetConnectionFormPool_Bottom(key);//Bottom级别
            }

    在向连接池申请一个连接资源时我们必须指定一个申请者,同时可以指定一个级别参数最后会根据不同的级别调用不同的策略,这些策略是在上面介绍过的4钟策略,下面不做过多的介绍。
(Java版本不在放出原因代码与C#完全相同)

  下面将逐一放出4种策略的完整代码不做过多解释,其功能以在前文介绍过

///<summary>//</summary>///<param name="key">申请者</param>///<returns>申请到的连接对象</returns>
protected DbConnection GetConnectionFormPool_ReadOnly(object=nullfor (int i =0; i < al_All.Count; i++=if (cs.Enable ==false|| cs.Allot ==false|| cs.UseDegree == _maxRepeatDegree || cs.IsUse ==truecontinuereturn GetConnectionFormPool_Return(key, cs, ConnLevel.ReadOnly); //返回得到的连接
            }return GetConnectionFormPool_Return(key, null///<summary>//</summary>///<param name="key">申请者</param>///<returns>申请到的连接对象</returns>
protected DbConnection GetConnectionFormPool_High(object=null=nullfor (int i =0; i < al_All.Count; i++=if (csTemp.Enable ==false|| csTemp.Allot ==false|| csTemp.UseDegree == _maxRepeatDegree)//不可以分配跳出本次循环。=nullcontinueif (csTemp.UseDegree ==0)//得到最合适的=breakelse//不是最合适的放置到最佳选择中if (cs !=nullif (csTemp.UseDegree <//与上一个最佳选择选出一个最佳的放置到cs中
                            cs =else=return GetConnectionFormPool_Return(key, cs, ConnLevel.High);//返回最合适的连接
        }///<summary>//</summary>///<param name="key">申请者</param>///<returns>申请到的连接对象</returns>
protected DbConnection GetConnectionFormPool_None(object=new ArrayList();
            ConnStruct cs =nullfor (int i =0; i < al_All.Count; i++=if (cs.Enable ==false|| cs.Allot ==false|| cs.UseDegree == _maxRepeatDegree)//不可以分配跳出本次循环。
continueif (cs.Allot ==true)
                    al.Add(cs);
            }if (al.Count ==0return GetConnectionFormPool_Return(key, null, ConnLevel.None);//发出异常
elsereturn GetConnectionFormPool_Return(key, ((ConnStruct)al[al.Count /2]), ConnLevel.None);//返回连接
        }///<summary>//</summary>///<param name="key">申请者</param>///<returns>申请到的连接对象</returns>
protected DbConnection GetConnectionFormPool_Bottom(object=null=nullfor (int i =0; i < al_All.Count; i++=if (csTemp.Enable ==false|| csTemp.Allot ==false|| csTemp.UseDegree == _maxRepeatDegree)//不可以分配跳出本次循环。=nullcontinueelse//不是最合适的放置到最佳选择中if (cs !=nullif (csTemp.UseDegree >//与上一个最佳选择选出一个最佳的放置到cs中
                            cs =else=return GetConnectionFormPool_Return(key, cs, ConnLevel.Bottom);//返回最合适的连接
        }

Java版本不在放出原因代码与C#完全相同

  在上面代码中所有策略都调用GetConnectionFormPool_Return方法。具体是什么呢?让我们来看看

///<summary>//</summary>///<param name="key">key</param>///<param name="cs">ConnStruct对象</param>///<param name="cl">级别</param>///<param name="readOnly">是否为只读属性</param>///<returns></returns>
private DbConnection GetConnectionFormPool_Return(objecttryif (cs ==nullthrownew Exception();
                cs.Repeat();
                hs_UseConn.Add(key, cs);
if (cl ===false=falsecatchthrownew OccasionExecption();//连接资源耗尽,或错误的访问时机。
            }finally{
                UpdateAttribute();//更新属性
            }return

该方法就是返回DbConnection对象,同时将获得的连接引用记数+1,在程序的第一行进行了一下内部错误判断如果是一个空的cs则抛出一个异常,该异常由方法体内接收并且将异常统一包装为OccasionExecption类型异常。hs_UseConn.Add(key, cs);是将被申请到的连接资源注册到连接池。同时也确定这个被申请走的连接是不是以只读方式申请的,如果是则将这个连接锁定。

cs.Allot =false=false;//设置连接不能使用引用记数技术

(Java版本不在放出原因代码与C#完全相同)

///<summary>//<param name="key">key表示数据库连接申请者</param>///</summary>
publicvoid DisposeConnection(objectlock=nullif (_ps ==if (!thrownew NotKeyExecption();//无法释放,不存在的key
                    cs = (ConnStruct)hs_UseConn[key];
                    cs.IsRepeat =trueif (cs.Allot ==falseif (cs.Enable ==true)
                            cs.Allot =true;
                    cs.Remove();
                    hs_UseConn.Remove(key);
                }elsethrownew PoolNotRunException();//服务未启动
            }//更新属性
        }

 在释放资源中可以说是上面申请资源的反方向操作其中不同点在于

if (cs.Enable ==true=true;

这段话的意思是如果连接对象已经失效则不更改会不会再次被分配的属性如果仍然有效则更改为会再次被分配,因为被包装后的Connection不会自己知道自己是否已经超过引用记数所要求的最大上限。

(Java版本不在放出原因代码与C#完全相同)

如何更新属性

///<summary>//</summary>
privatevoidint temp_readOnlyFormPool =0;//连接池已经分配多少只读连接
int temp_potentRealFormPool =0;//连接池中存在的实际连接数(有效的实际连接)
int temp_spareRealFormPool =0;//空闲的实际连接
int temp_useRealFormPool =0;//已分配的实际连接
int temp_spareFormPool = MaxConnectionFormPool;---------------------------------
lock=//---------------------------------
            ConnStruct cs =nullint n =0lock=for (int i =0; i < al_All.Count; i++=//只读
if (cs.Allot ==false&& cs.IsUse ==true&& cs.IsRepeat ==false)
                        temp_readOnlyFormPool++//有效的实际连接
if (cs.Enable ==true)
                        temp_potentRealFormPool++//空闲的实际连接
if (cs.Enable ==true&& cs.IsUse ==false)
                        temp_spareRealFormPool++//已分配的实际连接
if (cs.IsUse ==true)
                        temp_useRealFormPool++//目前可以提供的连接数
if (cs.Allot ==true)
                        temp_spareFormPool = temp_spareFormPool -else
                        temp_spareFormPool = temp_spareFormPool - _maxRepeatDegree;
                }= temp_readOnlyFormPool;
            _potentRealFormPool = temp_potentRealFormPool;
            _spareRealFormPool = temp_spareRealFormPool;
            _useRealFormPool = temp_useRealFormPool;
            _spareFormPool =

  在更新属性的方法里我们把每一个属性都做了一个对应的临时变量,为的就是防止在属性更新的时候被用户程序访问到属性更新期间的不准确信息,并且在连接池中所有属性更新操作都在此进行。

C#版本

///<summary>//</summary>///<param name="cs">被测试的ConnStruct</param>
privatevoid//此次被分配出去的连接是否在此次之后失效
if (cs.UseDegree == _maxRepeatDegree)
                cs.SetConnectionLost();//超过使用次数
if (cs.CreateTime.AddMinutes(_exist).Ticks <= DateTime.Now.Ticks)
                cs.SetConnectionLost();//连接超时
        }

Java版本

privatevoid TestConnStruct(ConnStruct cs) //此次被分配出去的连接是否在此次之后失效
if (cs.GetUseDegree() == _MaxRepeatDegree)
            cs.SetConnectionLost(); //超过使用次数
= Calendar.getInstance();
        c.setTime(cs.GetCreateTime());
        c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) +if (c.getTime().after(new Date()))
            cs.SetConnectionLost(); //连接超时
    }

使用线程管理连接池

threadCreate

   前面已经很多次的提到threadCreate和threadCheck。那它们2究竟怎么运行的呢?还是先让我们看一看threadCreate。

///<summary>//</summary>
privatevoidbool join =falseint createThreadProcessTemp_inside = createThreadProcessTemp;
            _ps =while (true=false=if (createThreadProcessRun ==false{//遇到终止命令try{ threadCreate.Join(); }catch (Exception e) { }elseif (createThreadMode ==0//------------------------begin mode  创建模式
//------------------------end mode
                    }elseif (createThreadMode ==1//------------------------begin mode  增加模式
//------------------------end mode
                    }else=true//-------------------------------------------------------------------------
if (join ==true{
                        UpdateAttribute();//更新属性
try{
                            createThreadProcessTemp =0;
                            threadCreate.Join();
                        }catch{ createThreadProcessTemp_inside = createThreadProcessTemp; }//得到传入的变量
             }

  在线程中每次执行循环前都要判断自己是不是被要求终止,如果自己被要求终止则无条件将自己立即阻塞上,注意线程仅仅被阻塞没有被终止。
  在下面一组createThreadMode 运行模式判断中如果出现不属于0或者1的模式则放出自己被阻止的信息。
  在最后线程即将被阻止时将执行更新属性操作并且将自己的运行参数设置为0,因为当运行参数为0时即使线程被唤醒,它也将不会创建任何连接,最终自己还是被阻止。

threadCheck

            ConnStruct cs =null//关闭自己
            isThreadCheckRun =true//如果正在执行创建连接则退出
if (threadCreate.ThreadState !=return//------------------------------------------------------
lockint n =0for (int i =0; i < al_All.Count; i++= (ConnStruct)al_All[i];
                    TestConnStruct(cs);//测试
if (cs.Enable ==false&& cs.RepeatNow ==0)//没有引用的失效连接{
                        cs.Close();//关闭它
                        al_All.Remove(cs);//删除
                    }//------------------------------------------------------
            UpdateAttribute();//更新属性
if (_spareRealFormPool < _keepRealConnection)//保留空闲实际连接数不足
                createThreadProcessTemp = GetNumOf(_realFormPool, _seepConnection, _maxConnection);
else
                createThreadProcessTemp =0;
if (createThreadProcessTemp !=0//启动创建线程,工作模式1
                createThreadMode =1;
                threadCreate.Interrupt();
            }=false//打开自己

  在C#中由于Timer拥有自己的特性,所以每次触发事件的时候都将自己关闭,以保证同时只有一个事件处理过程在运行,当处理结束时在将自己打开。
  threadCheck线程的最主要目的就是测试当前所有实际连接是否有满足失效的连接,如果有失效的连接就将其设置为失效,同时清理已经没有任何引用的失效连接。在做完这些操作之后它还要得到是否要创建补充的连接如果需要创建补充的连接则在将这个数值传递给threadCreate并将之启动。
  其中可能出现的问题就是,如果threadCreate已经启动我们在传递参数给它那可能会引发threadCreate的处理异常,因此在threadCheck过程中会首先判断threadCreate是否在阻止中如果不在阻止状态就return等待下次threadCheck的事件。
  GetNumOf是为了得到应该创建的个数,代码实现如下

///<summary>//</summary>
privateint GetNumOf(int nowNum, int seepNum, intif (maxNum >= nowNum +returnelsereturn maxNum -

下面是threadCheck的java版,因为前文说了在Java下没有Timer类有的Timer类还仅仅在AWT下工作,因此我们要先做一个Timer类,具体如下

privateclass CheckThreadProcess extends Thread privatelong _Interval =100; //执行间隔
privateboolean timeSize=falsepublicvoid=trueif (this.getState()==thiselseif (this.getState()==thispublicvoid=falsewhile(this.getState()!=Thread.State.WAITING){ }publicvoidpublicvoid run() while (true) trythisif (timeSize ==trueelsethiscatch (InterruptedException ex1) { }/**@param*/publicvoid setInterval(long value) publiclong getInterval() 

其中aRun方法就是threadCheck在C#下的处理过程代码如下

publicvoid=null//如果正在执行创建连接则退出
if (threadCreate.getState() !=return//------------------------------------------------------
synchronizedfor (int i =0; i < al_All.size(); i++= (ConnStruct)al_All.get(i);
                    TestConnStruct(cs);//测试
if (cs.GetEnable() ==false&& cs.GetRepeatNow() ==0)//没有引用的失效连接{
                        cs.Close();//关闭它
                        al_All.remove(cs);//删除
                    }//------------------------------------------------------
            UpdateAttribute();//更新属性
if (_SpareRealFormPool < _KeepRealConnection)//保留空闲实际连接数不足
                threadCreate.createThreadProcessTemp = GetNumOf(_RealFormPool, _SeepConnection, _MaxConnection);
else
                threadCreate.createThreadProcessTemp =0//if (threadCreate.createThreadProcessTemp != 0)
    System.out.println("创建" + threadCreate.createThreadProcessTemp);
//    System.out.println(threadCreate.getState()+ " this " + this.getState());
//}
if (threadCreate.createThreadProcessTemp !=0//启动创建线程,工作模式1
                threadCreate.createThreadMode =1;
                threadCreate.interrupt();
            }

至此一个拥有引用记数,集合线程管理的数据库连接池就完成了。下面是连接池的中被包装的连接代码

其他

///<summary>//</summary>
publicclass///<summary>//</summary>///<param name="dbc">数据库连接</param>///<param name="cte">连接类型</param>
public ConnStruct(DbConnection dbc, ConnTypeEnum cte)
===///<summary>//</summary>///<param name="dt">连接创建时间</param>///<param name="dbc">数据库连接</param>///<param name="cte">连接类型</param>
public ConnStruct(DbConnection dbc, ConnTypeEnum cte, DateTime dt)
===//--------------------------------------------------------------------
privatebool enable =true;//是否失效
privatebool use =false;//是否正在被使用中
privatebool allot =true;//表示该连接是否可以被分配
private DateTime createTime = DateTime.Now;//创建时间
privateint useDegree =0;//被使用次数
privateint repeatNow =0;//当前连接被重复引用多少
privatebool isRepeat =true;//连接是否可以被重复引用,当被分配出去的连接可能使用事务时,该属性被标识为true
private ConnTypeEnum connType = ConnTypeEnum.None;//连接类型
private DbConnection connect =null;//连接对象
privateobject obj =null;//连接附带的信息
属性部分///<summary>//</summary>
publicvoid{ connect.Open(); }///<summary>//</summary>
publicvoid{ connect.Close(); }///<summary>//</summary>
publicvoid{ enable =false; allot =false; }///<summary>//</summary>
publicvoidlock (thisif (enable ==false)//连接可用
thrownew ResLostnExecption();//连接资源已经失效
if (allot ==false)//是否可以被分配
thrownew AllotExecption();//连接资源不可以被分配
if (use ==true&& isRepeat ==falsethrownew AllotAndRepeatExecption();//连接资源已经被分配并且不允许重复引用
                repeatNow++;//引用记数+1
                useDegree++;//被使用次数+1
                use =true;//被使用
            }///<summary>//</summary>
publicvoidlock (thisif (enable ==false)//连接可用
thrownew ResLostnExecption();//连接资源已经失效
if (repeatNow ==0thrownew RepeatIsZeroExecption();//引用记数已经为0
                repeatNow--;//引用记数-1
if (repeatNow ==0=false;//未使用
else=true;//使用中
            }///<summary>//</summary>
publicvoid=false;
            connect.Close();
            connect =null

上面代码的Java实现

/// <summary>/ 连接池中的一个连接类型/ </summary>publicclass ConnStruct privateboolean _enable =true; //是否失效
privateboolean _isUse =false; //是否正在被使用中
privateboolean _allot =true; //表示该连接是否可以被分配
private Date _createTime; //创建时间
privateint _useDegree =0; //被使用次数
privateint _repeatNow =0; //当前连接被重复引用多少
privateboolean _isRepeat =true; //连接是否可以被重复引用,当被分配出去的连接可能使用事务时,该属性被标识为true
privateint _connType; //连接类型
private Connection _connect =null; //连接对象public ConnStruct(Connection dbc) new/**@param@param*/public ConnStruct(Connection dbc, Date dt) /**@param@param*/privatevoid InitConnStruct(Connection dbc, Date dt) ==//--------------------------------------------------------------------/**@return*/publicboolean GetAllot() /**@param*/publicvoid SetAllot(boolean value) /**@return*/publicboolean GetEnable() /**@return*/publicboolean GetIsUse() public Date GetCreateTime() publicint GetUseDegree() publicint GetRepeatNow() public Connection GetConnection() publicboolean GetIsRepeat() /**@param*/publicvoid SetIsRepeat(boolean value) publicint GetConnType() publicvoid Close() trycatch (Exception e) {}publicvoid SetConnectionLost() =false=false/**@throws@throws@throws*/publicsynchronizedvoid Repeat() throws ResLostnException, AllotException,
            AllotAndRepeatException if (_enable ==false) //连接可用
thrownew ResLostnException(); //连接资源已经失效
if (_allot ==false) //是否可以被分配
thrownew AllotException(); //连接资源不可以被分配
if (_isUse ==true&& _isRepeat ==falsethrownew AllotAndRepeatException(); //连接资源已经被分配并且不允许重复引用
        _repeatNow++; //引用记数+1
        _useDegree++; //被使用次数+1
        _isUse =true; //被使用
    }/**@throws@throws*/publicsynchronizedvoid Remove() throws ResLostnException,
            RepeatIsZeroException if (_enable ==false) //连接可用
thrownew ResLostnException(); //连接资源已经失效
if (_repeatNow ==0thrownew RepeatIsZeroException(); //引用记数已经为0
        _repeatNow--; //引用记数-1
if (_repeatNow ==0=false; //未使用
else=true; //使用中
    }/// <summary>
/// 释放资源
/// </summary>publicvoid Dispose() =falsetrycatch (Exception e) {}=null
lockif (al_All.Count < createThreadProcessTemp_inside)
  al_All.Add(CreateConnection(_connString, _connType));
 else=true

  在模式0中线程将一直创建连接,直到被创建的连接达到最小连接数目,具体实现方法是将最小要创建的连接存入al_All,直到al_All的元素个数达到最小连接数才放出自己被阻止的消息。

lockif (createThreadProcessTemp_inside !=0--else=true

  在模式1中线程将一直创建连接,直到被创建的连接达到被要求的连接数目,具体实现方法是将最小要创建的连接存入createThreadProcessTemp_inside临时变量,然后每次创建一个连接就自减1直至到0时放出自己被阻止的消息。

以下是java的代码实现

privateclass CreateThreadProcess extends Thread publicint createThreadMode =0; //创建线程工作模式
publicint createThreadProcessTemp =0; //需要创建的连接数
publicboolean createThreadProcessRun =false; //是否决定创建线程将继续工作,如果不继续工作则线程会将自己处于阻止状态
publicvoid run() boolean join =falseint createThreadProcessTemp_inside = createThreadProcessTemp;
            _PoolState =while (true=false=if (createThreadProcessRun ==false{//遇到终止命令trythis.join();//中断自己                    }catch (Exception e) elseif (createThreadMode ==0//------------------------begin mode  创建模式
synchronizedif (al_All.size() < createThreadProcessTemp_inside)
                                al_All.add(CreateConnectionTemp(_ConnString,_DriveString,_userID,_password));
else
                                join =true//------------------------end mode
                    }elseif (createThreadMode ==1//------------------------begin mode  增加模式
synchronizedif (createThreadProcessTemp_inside !=0{
                                createThreadProcessTemp_inside--;
                                al_All.add(CreateConnectionTemp(_ConnString,_DriveString,_userID,_password));
                            }else
                                join =true//------------------------end mode
                    }else=true//-------------------------------------------------------------------------
if (join ==true{
                        UpdateAttribute();//更新属性try{
                            createThreadProcessTemp =0this.join(); //中断自己                        }catch (Exception e) {
                            createThreadProcessTemp_inside = createThreadProcessTemp;
                        }//得到传入的变量
                    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值