`
chengzhisheng
  • 浏览: 53406 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

1. 引言

  近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机

  应用程序已从传统的桌面应用转到Web应用。基于B/S(Browser/Server)架构的3层开发模式逐渐取代C/S(Client/Server)架构的开发模式,成为开发企业级应用和电子商务普遍采用的技术。在Web应用开发的早期,主要使用的技术是CGIASPPHP等。之后,Sun公司推出了基于Java语言的Servlet+Jsp+JavaBean技术。相比传统的开发技术,它具有跨平台﹑安全﹑有效﹑可移植等特性,这使其更便于使用和开发。

  Java应用程序访问数据库的基本原理

  在Java语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁,

  即Java语言通过JDBC技术访问数据库。JDBC是一种“开放”的方案,它为数据库应用开发人员﹑数据库前台工具开发人员提供了一种标准的应用程序设计接口,使开发人员可以用纯Java语言编写完整的数据库应用程序。JDBC提供两种API,分别是面向开发人员的API和面向底层的JDBC驱动程序API,底层主要通过直接的JDBC驱动和JDBC-ODBC桥驱动实现与数据库的连接。

  一般来说,Java应用程序访问数据库的过程(如图1所示)是:

  ①装载数据库驱动程序;

  ②通过JDBC建立数据库连接;

  ③访问数据库,执行SQL语句;

  ④断开数据库连接。

  图1 Java数据库访问机制

JDBC作为一种数据库访问技术,具有简单易用的优点。但使用这种模式进行Web应用

  程序开发,存在很多问题:首先,每一次Web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的Web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。还有,这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
  数据库连接池(connection pool)的工作原理
  1、基本概念及原理
由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理。我们知道,
  对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图2。


图2 连接池的基本工作原理

 2、服务器自带的连接池

  JDBC的API中没有提供连接池的方法。一些大型的WEB应用服务器如BEA的WebLogic和IBM的WebSphere等提供了连接池的机制,但是必须有其第三方的专用类方法支持连接池的用法。
  连接池关键问题分析
  1、并发问题
  为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java语言自身提供了对并发管理的支持,使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字,如:
public synchronized Connection getConnection()
  2、多数据库服务器和多用户
  对于大型的企业级应用,常常需要同时连接不同的数据库(如连接OracleSybase)。如何连接不同的数据库呢?我们采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url地址(<poolName.url>)﹑用户名(<poolName.user>)﹑密码(<poolName.password>)等信息。如tx.url=192.168.1.123:5000/tx_it,tx.user=cyl,tx.password=123456。根据资源文件提供的信息,创建多个连接池类的实例,每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字,通过不同的名字来管理不同的连接池。
  对于同一个数据库有多个用户使用不同的名称和密码访问的情况,也可以通过资源文件处理,即在资源文件中设置多个具有相同url地址,但具有不同用户名和密码的数据库连接信息。
  3、事务处理
  我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
Java语言中,Connection类本身提供了对事务的支持,可以通过设置Connection的AutoCommit属性为false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用,就必须提供相应的事务支持机制。可采用每一个事务独占一个连接来实现,这种方法可以大大降低事务管理的复杂性。
  4、连接池的分配与释放
  连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
  对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时,系统首先检查空闲池内有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断,如果可用就分配给用户,如不可用就把这个连接从空闲池删掉,重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有达到,就新建一个连接,如果已经达到,就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户,如果等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数,当使用完后再返还给空闲池。对于空闲连接的状态,可开辟专门的线程定时检测,这样会花费一定的系统开销,但可以保证较快的响应速度。也可采取不开辟专门线程,只是在分配前检测的方法。
  5、连接池的配置与维护
  连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConn)和最大连接数(maxConn)来控制连接池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过反复测试,找到最佳点。
  如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。
连接池的实现
  1、连接池模型
  本文讨论的连接池包括一个连接池类(DBConnectionPool)和一个连接池管理类(DBConnetionPoolManager)和一个配置文件操作类(ParseDSConfig)。连接池类是对某一数据库所有连接的“缓冲池”,主要实现以下功能:①从连接池获取或创建可用连接;②使用完毕之后,把连接返还给连接池;③在系统关闭前,断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。(5)当多数据库时,且数据库是动态增加的话,将会加到配置文件中。
  连接池管理类是连接池类的外覆类(wrapper),符合单例模式,即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理,具有以下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,创建连接池对象;③为方便管理多个连接池对象,为每一个连接池对象取一个名字,实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况,以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理,如系统需要连接不同的数据库,或连接相同的数据库但由于安全性问题,需要不同的用户使用不同的名称和密码。
          2、连接池实现(经过本人改版,可以适用多数据库类型的应用以及一种数据库类型多个数据库且数据   库的数量可以动态增加的应用程序)
          1),DBConnectionPool.java    数据库连接池类
          2),DBConnectionManager .java    数据库管理类
          3),DSConfigBean .java                 单个数据库连接信息Bean
          4),ParseDSConfig.java                 操作多(这个'多'包括不同的数据库和同一种数据库有多个数据库)
                                                             数据 配置文件xml
          5),ds.config.xml                            数据库配置文件xml
          原代码如下:
         DBConnectionPool.java   
         ----------------------------------------------------------
       /**
* 数据库连接池类
*/
package com.chunkyo.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
/**
* @author chenyanlin
*
*/
public class DBConnectionPool implements TimerListener {
private Connection con=null;
private int inUsed=0;     //使用的连接数
private ArrayList freeConnections = new ArrayList();//容器,空闲连接
private int minConn;      //最小连接数
private int maxConn;      //最大连接
private String name;      //连接池名字
private String password; //密码
private String url;       //数据库连接地址
private String driver;    //驱动
private String user;      //用户名
public Timer timer;       //定时
/**
   *
   */
public DBConnectionPool() {
   // TODO Auto-generated constructor stub
}
/**
   * 创建连接池
   * @param driver
   * @param name
   * @param URL
   * @param user
   * @param password
   * @param maxConn
   */
public DBConnectionPool(String name, String driver,String URL, String user, String password, int maxConn)
{
   this.name=name;
   this.driver=driver;
   this.url=URL;
   this.user=user;
   this.password=password;
   this.maxConn=maxConn;
}
/**
   * 用完,释放连接
   * @param con
   */
public synchronized void freeConnection(Connection con)
{
   this.freeConnections.add(con);//添加到空闲连接的末尾
   this.inUsed--;
}
/**
   * timeout   根据timeout得到连接
   * @param timeout
   * @return
   */
public synchronized Connection getConnection(long timeout)
{
   Connection con=null;
   if(this.freeConnections.size()>0)
   {
    con=(Connection)this.freeConnections.get(0);
    if(con==null)con=getConnection(timeout); //继续获得连接
   }
   else
   {
    con=newConnection(); //新建连接
   }
   if(this.maxConn==0||this.maxConn<this.inUsed)
   {
    con=null;//达到最大连接数,暂时不能获得连接了。
   }
   if(con!=null)
   {
    this.inUsed++;
   }
   return con;
}
/**
   *
   * 从连接池里得到连接
   * @return
   */
public synchronized Connection getConnection()
{
   Connection con=null;
   if(this.freeConnections.size()>0)
   {
    con=(Connection)this.freeConnections.get(0);
    this.freeConnections.remove(0);//如果连接分配出去了,就从空闲连接里删除
    if(con==null)con=getConnection(); //继续获得连接
   }
   else
   {
    con=newConnection(); //新建连接
   }
   if(this.maxConn==0||this.maxConn<this.inUsed)
   {
    con=null;//等待 超过最大连接时
   }
   if(con!=null)
   {
    this.inUsed++;
    System.out.println("得到 "+this.name+" 的连接,现有"+inUsed+"个连接在使用!");
   }
   return con;
}
/**
   *释放全部连接
   *
   */
public synchronized void release()
{
   Iterator allConns=this.freeConnections.iterator();
   while(allConns.hasNext())
   {
    Connection con=(Connection)allConns.next();
    try
    {
     con.close();
    }
    catch(SQLException e)
    {
     e.printStackTrace();
    }
   
   }
   this.freeConnections.clear();
   
}
/**
   * 创建新连接
   * @return
   */
private Connection newConnection()
{
   try {
    Class.forName(driver);
    con=DriverManager.getConnection(url, user, password);
   } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    System.out.println("sorry can't find db driver!");
   } catch (SQLException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
    System.out.println("sorry can't create Connection!");
   }
   return con;
  
}
/**
   * 定时处理函数
   */
public synchronized void TimerEvent()
{
      //暂时还没有实现以后会加上的
}
/**
   * @param args
   */
public static void main(String[] args) {
   // TODO Auto-generated method stub
}
/**
   * @return the driver
   */
public String getDriver() {
   return driver;
}
/**
   * @param driver the driver to set
   */
public void setDriver(String driver) {
   this.driver = driver;
}
/**
   * @return the maxConn
   */
public int getMaxConn() {
   return maxConn;
}
/**
   * @param maxConn the maxConn to set
   */
public void setMaxConn(int maxConn) {
   this.maxConn = maxConn;
}
/**
   * @return the minConn
   */
public int getMinConn() {
   return minConn;
}
/**
   * @param minConn the minConn to set
   */
public void setMinConn(int minConn) {
   this.minConn = minConn;
}
/**
   * @return the name
   */
public String getName() {
   return name;
}
/**
   * @param name the name to set
   */
public void setName(String name) {
   this.name = name;
}
/**
   * @return the password
   */
public String getPassword() {
   return password;
}
/**
   * @param password the password to set
   */
public void setPassword(String password) {
   this.password = password;
}
/**
   * @return the url
   */
public String getUrl() {
   return url;
}
/**
   * @param url the url to set
   */
public void setUrl(String url) {
   this.url = url;
}
/**
   * @return the user
   */
public String getUser() {
   return user;
}
/**
   * @param user the user to set
   */
public void setUser(String user) {
   this.user = user;
}

 

 

 

分享到:
评论
2 楼 01404421 2009-04-16  
正在学习,谢谢LZ分享了,提出个小意见,当使用连接池的程序在关闭了一个连接后又释放,再放入Pool中就会出问题,如一下代码:
一,正常
DBConnectionPool dbp=new DBConnectionPool("rain", "com.mysql.jdbc.Driver","jdbc:mysql://127.0.0.1:3306/wer", "root", "root", 3);
		Connection conn0= dbp.getConnection();
				
		dbp.freeConnection(conn0);
		
		Connection conn1= dbp.getConnection();
		java.sql.Statement st=conn1.createStatement();

二,这样会出异常
DBConnectionPool dbp=new DBConnectionPool("rain", "com.mysql.jdbc.Driver","jdbc:mysql://127.0.0.1:3306/wer", "root", "root", 3);
		Connection conn0= dbp.getConnection();
	    conn0.close();	
		dbp.freeConnection(conn0);
		
		Connection conn1= dbp.getConnection();
		java.sql.Statement st=conn1.createStatement();

因为conn0已经关闭了这个连接,下面再使用时就出出错
LZ应该在freeConnection()方法中加入Connection是否关闭的判断,更改后代码如下:
public synchronized void freeConnection(Connection con) throws SQLException {
		if(!con.isClosed()){
		this.freeConnections.add(con);// 添加到空闲连接的末尾
		this.inUsed--;
		}
	}
1 楼 foolfisher 2008-11-13  
收了

相关推荐

    浅谈java连接池

    java实现连接池的代码,从底层连接连接池的实现1

    java连接池的配置

    java连接池的配置 里面有C3P0连接池在tomcat中的详细配置 Java反射机制总结,tomcat下配置数据库连接池DBCP、C3P0、Proxool 总结spring下配置dbcp,c3p0,proxool数据源链接池

    Java 连接池源码

    个人实现Java连接池源码 Java 连接池源码 仅供参考

    Java 连接池【示例】

    Java 连接池【示例】 Java 连接池【示例】 Java 连接池【示例】

    java连接池文件,连接池

    java连接池文件 java连接池文件 java连接池文件

    java连接池实例.doc

    java连接池实例.doc java连接池实例.doc

    模拟的Java连接池

    模拟的Java连接池 模拟的Java连接池 模拟的Java连接池 模拟的Java连接池

    mongodb Java连接池

    mongodb Java连接池配置 用于避免Java连接mongodb数据库数过高引起的一系列问题

    Java连接池评估报告

    Java连接池评估报告

    Java 连接池多种方式配置

    Java 连接池多种方式配置,这是本人多方收集而得到的,包括了在java 常用框架下怎样配置Java 连接池.欢迎大家下载

    使用Java编写的RabbitMQ连接池方法

    RabbitMQ客户连接池的Java实现。我们刚开始也是采用这种方式来实现的,但做压力测试时,发现这种每次新建Connection和新建Channel是非常耗时的,在大并发下,一般都要8毫秒左右,慢的话,好多都是几十毫秒。因此我们...

    Java连接池的实现

    资源名称:Java连接池的实现资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。

    java连接池有关jar:commons-pool-1.2.jar+commons-pool-1.3.jar+commons-pool.jar

    java连接池;java连接池jar;commons-pool-1.2.jar;commons-pool-1.3.jar+commons-pool.jar;java连接池jar包java连接池;java连接池jar;commons-pool-1.2.jar;commons-pool-1.3.jar+commons-pool.jar;java连接池jar包...

    强大的Java连接池类 for Java 附带源代码。

    我自行开发的一套Java平台的数据库连接池类,内部使用的是线程控制,其详细原理说明在CSDN进其将有所发布,届时也有源代码结实说明,该压缩文件中包含连接池运行的jar文件和其帮助手册中文版,(未竟严格代码测试) ...

    JAVA+ACCESS连接池(精典)

    JAVA+ACCESS连接池JAVA+ACCESS连接池JAVA+ACCESS连接池JAVA+ACCESS连接池JAVA+ACCESS连接池JAVA+ACCESS连接池JAVA+ACCESS连接池

    java数据库连接池

    java数据库连接池,是一个简单实现数据库连接池的类

    java连接池可以自己更换数据库

    java连接池,可以自己更换数据库。管理类ConnectionPool支持对一个或多个由属性文件定义的数据库连接

    java ftp连接池

    在网上找了好久没有现成的ftp连接池jar包,自己花了一些时间实现了一个简单的连接池,用了一段时间稳定性还可以。

    强大的Java连接池类 for .Net

    我自行开发的一套Java平台的数据库连接池类,内部使用的是线程控制,其详细原理说明在CSDN进其将有所发布,届时也有源代码结实说明,该压缩文件中包含连接池运行的jar文件和其帮助手册中文版,未竟严格代码测试) ...

Global site tag (gtag.js) - Google Analytics