第十一章 连接池(Connection Pooling)

在基本的DataSource实现中,客户端的Connection对象和物理数据库连接之间存在1:1的对应关系。 当Connection对象关闭时,物理连接被丢弃。 因此,每个客户端会话都会产生打开,初始化和关闭物理连接的开销

连接池通过维护可跨客户端会话重用的物理数据库连接缓存来解决此问题。 连接池大大提高了性能和可扩展性,特别是在三层环境中,多个客户端可以共享较少数量的物理数据库连接。 在图11-1中,JDBC驱动程序提供了应用程序服务器用于构建和管理连接池的ConnectionPoolDataSource的实现。

用于管理连接池的算法的实现事特定的,随应用服务器而异。 应用程序服务器为其客户端提供DataSource接口的实现,使连接池对客户端透明。 因此,客户端在使用与之前相同的JNDI和DataSource API时可以获得更好的性能和可扩展性

Paste_Image.png

以下部分介绍ConnectionPoolDataSource接口,PooledConnection接口和ConnectionEvent类。 这些在客户端使用的DataSource和Connection接口下运行的程序被并入到典型连接池实现的逐步描述中

本章还介绍了基本的DataSource对象与实现连接池的一些重要区别。 此外,它还讨论了池连接如何维护可重用的PreparedStatement对象池

尽管本章中讨论的大部分都假设了三层环境,但连接池在两层环境中也是相关的。 在两层环境中,JDBC驱动程序实现了DataSource和ConnectionPoolDataSource接口。 此实现允许打开和关闭多个连接的应用程序从连接池中受益

11.1 ConnectionPoolDataSource and PooledConnection

通常,JDBC驱动程序实现ConnectionPoolDataSource接口,应用程序服务器使用它来获取PooledConnection对象。 代码示例11-1显示了getPooledConnection方法的两个版本的签名

public interface ConnectionPoolDataSource {
PooledConnection getPooledConnection() throws
SQLException;
PooledConnection getPooledConnection(String user,
String password) throws SQLException;
...
}

PooledConnection对象表示与数据源的物理连接。 PooledConnection的JDBC驱动程序的实现封装了维护该连接的所有细节

应用服务器在DataSource接口的实现中缓存并重用PooledConnection对象。 当客户端调用DataSource.getConnection方法时,应用程序服务器使用物理PooledConnection对象来获取逻辑Connection对象。 代码示例11-2显示了PooledConnection接口定义。

public interface PooledConnection {
Connection getConnection() throws SQLException;
void close() throws SQLException;
void addConnectionEventListener(
ConnectionEventListener listener);
void addStatementEventListener(
StatementEventListener listener);
void removeConnectionEventListener(
ConnectionEventListener listener);
void removeStatementEventListener(
StatementEventListener listener);
}

当应用程序使用连接完成时,它将使用Connection.close方法来关闭逻辑连接。 这将关闭逻辑连接,但不会关闭物理连接。 而是将物理连接返回到池,以便可以重新使用

备注:当使用Connection pooling时调用Connection.closed时,由Connection.setClientInfo设置的任何属性都将被清除。

11.2 Connection Events

回想一下,当应用程序调用Connection.close方法时,底层物理连接(PooledConnection对象)可用于重用。 JavaBeans风格的事件用于通知连接池管理器(应用程序服务器)可以回收PooledConnection对象

为了通知PooledConnection对象上的事件,连接池管理器必须实现ConnectionEventListener接口,然后由该PooledConnection对象注册为侦听器 ,ConnectionEventListener接口定义了以下两种方法,它们对应于PooledConnection对象可能发生的两种事件:

  • connectionClosed :当与此PooledConnection对象关联的逻辑连接对象关闭时触发,即称为方法Connection.close的应用程序
  • connectionErrorOccurred :当致命错误(如服务器崩溃)导致连接丢失时触发

连接池管理器使用PooledConnection.addConnectionEventListener方法将自身注册为PooledConnection对象的侦听器。 通常,在将Connection对象返回给应用程序之前,连接池管理器将自身注册为ConnectionEventListener。

当相应的事件发生时,驱动程序调用ConnectionEventListener方法connectionClosed和connectionErrorOccurred。 两个方法都将ConnectionEvent对象作为参数,可用于确定哪个PooledConnection对象已关闭或出现错误。 当JDBC应用程序关闭其逻辑连接时,JDBC驱动程序通过调用监听器的connectClosed方法的实现来通知连接池管理器(监听器)。 此时,连接池管理器可以将PooledConnection对象返回到池中以供重用

发生错误时,JDBC驱动程序通过调用其connectionErrorOccurred方法通知侦听器,然后将SQLException对象抛出该应用程序,以通知它相同的错误。 在发生致命错误的情况下,坏池池连接对象不会返回到池中。 而是连接池管理器调用PooledConnection对象上的PooledConnection.close方法来关闭物理连接

11.3 三层环境中的连接池

以下步骤序列概述了当JDBC客户端从实现连接池的DataSource对象请求连接时会发生什么:

  • 这客户端呼叫 DataSource.getConnection.
  • 提供DataSource实现的应用程序服务器在其连接池中查找是否有合适的PooledConnection对象 - 物理数据库连接可用。确定给定PooledConnection对象的适用性可能包括匹配客户端的用户认证信息或应用程序类型以及使用其他实现特定标准。 与管理连接池相关联的查找方法和其他方法是特定于应用程序服务器的
  • 如果没有适当的PooledConnection对象可用,应用程序服务器将调用ConnectionPoolDataSource.getPooledConnection方法来获取新的物理连接。 实现ConnectionPoolDataSource的JDBC驱动程序创建一个新的PooledConnection对象并将其返回给应用程序服务器。
  • 无论PooledConnection是从池中检索还是新创建,应用程序服务器都会进行一些内部簿记来指示物理连接正在使用中
  • 应用程序服务器调用方法PooledConnection.getConnection以获取逻辑连接对象。 这个逻辑的Connection对象实际上是一个物理PooledConnection对象的“句柄”,当连接池生效时,DataSource.getConnection方法返回的是这个句柄
  • 应用程序服务器通过调用PooledConnection.addConnectionEventListener方法将自身注册为ConnectionEventListener。 这样做是为了在PooledConnection对象可用于重用时通知应用程序服务器
  • 逻辑Connection对象返回给JDBC客户端,JDBC客户端使用与基本DataSource情况相同的Connection API。 请注意,底层PooledConnection对象不能重用,直到客户端调用方法Connection.close。
  • 连接池也可以在没有应用服务器的两层环境中实现。 在这种情况下,JDBC驱动程序提供对客户端可见的DataSource的实现以及底层的ConnectionPoolDataSource实现

11.4 DataSource实现和连接池

除了提高性能和可扩展性之外,JDBC应用程序不应该在访问实现连接池的DataSource对象之间看不到任何差异。 但是,在应用程序服务器和驱动程序级别的实现中存在一些重要的区别

基本的DataSource实现,即不实现连接池的实现通常由JDBC驱动程序供应商提供。 在基本的DataSource实现中,以下是真实的

  • DataSource.getConnection方法创建一个新的Connection对象,它表示一个物理连接,并封装了所有的工作来设置和管理该连接。
  • Connection.close方法关闭物理连接并释放相关资源

在包含连接池的DataSource实现中,幕后有很多事情发生。 在这种实现中,以下是正确的

  • DataSource实现包括一个实现特定的连接池模块,用于管理PooledConnection对象的缓存。
    DataSource对象通常由应用程序服务器实现,作为ConnectionPoolDataSource和PooledConnection接口的驱动程序实现之上的层
  • DataSource.getConnection方法调用PooledConnection.getConnection以获取底层物理连接的逻辑句柄。 仅当连接池中没有可用的连接时,才会产生设置新物理连接的开销。 当需要新的物理连接时,连接池管理器将调用ConnectionPoolDataSource方法getPooledConnection来创建一个。 管理物理连接的工作被委派给PooledConnection对象
  • Connection.close方法关闭逻辑句柄,但是保持物理连接。 通知连接池管理器现在可以重用基础PooledConnection对象。 如果应用程序尝试重用逻辑句柄,则Connection实现将抛出一个SQLException
    单个物理PooledConnection对象可能会在其生命周期内生成许多逻辑连接对象。 对于给定的PooledConnection对象,只有最近生成的逻辑连接对象才有效。 当调用关联的PooledConnection.getConnection方法时,任何先前存在的Connection对象都将自动关闭。 在这种情况下,不会通知侦听器(连接池管理器)
  • 连接池管理器通过调用PooledConnection.close方法来关闭物理连接。 通常仅在某些情况下才会调用此方法:当应用程序服务器正在进行有序关闭时,当重新初始化连接缓存时,或者当应用程序服务器收到指示连接上发生不可恢复的错误的事件时

11.5 部署

部署实现连接池的DataSource对象需要将客户端可见的DataSource对象和底层的ConnectionPoolDataSource对象注册到基于JNDI的命名服务

com.acme.appserver.PooledDataSource ds =
new com.acme.appserver.PooledDataSource();
ds.setDescription(“Datasource with connection pooling”);
// Reference the previously registered ConnectionPoolDataSource
ds.setDataSourceName(“jdbc/pool/bookserver_pool”);
// Register the DataSource implementation with JNDI, using the logical // name “jdbc/bookserver”.
Context ctx = new InitialContext();
ctx.bind(“jdbc/bookserver”, ds);

11.6 通过池连接重用语句

DBC规范提供对语句池的支持。 此功能允许应用程序以与重用连接大致相同的方式重用PreparedStatement对象,可通过池化连接获得

图11-2提供了一个PreparedStatement池如何与PooledConnection对象关联的逻辑视图。 与PooledConnection对象本身一样,PreparedStatement对象可以通过多个逻辑连接以透明的方式重用

11-2

在图11-2中,连接池和语句池由应用服务器实现。 但是,该功能也可以由驱动程序或底层数据源来实现。 这种对语句池的讨论是为了允许任何这些实现。

11.6.1 使用 池语句

如果池化连接重用语句,则重用必须对应用程序完全透明。 换句话说,从应用程序的角度来看
使用参与语句池的PreparedStatement对象与使用不一致的PreparedStatement对象完全相同。 声明保持开放,完全在封面下重复使用,因此应用代码没有变化。 如果一个应用程序关闭PreparedStatement对象,它仍然必须调用Connection.prepareStatement才能再次使用它。 语句池唯一可见的效果是性能的提高

一个应用程序可以通过调用DatabaseMetaData方法supportsStatementPooling来确定数据源是否支持语句池。 如果返回值为true,则应用程序可以选择使用PreparedStatement对象,知道它们可能被汇总

在许多情况下,重用语句是一个重要的优化。 对于复杂的准备语句尤其如此。 但是,还应该指出的是,大量的公开声明可能会对资源的使用产生不利影响

11.6.2 关闭 池语句

一个应用程序关闭一个pooled语句完全一样,它关闭一个非pooled语句。 无论是否合并,已关闭的语句不再可供应用程序使用,并且尝试重用它将导致抛出异常
以下方法可以关闭pooled语句:

  • Statement.close :被调用通过一个应用; 如果语句正在池中,请关闭应用程序使用的逻辑语句,但不关闭正在池中的物理语句
  • Connection.close :被调用通过一个应用,
    无连接池:关闭物理连接和由该连接创建的所有语句。 这是必要的,因为垃圾收集机制无法检测何时可以释放外部管理的资源
    有连接池:关闭逻辑连接及其返回的逻辑语句,但会打开底层Pooled Connection对象和任何关联的pooled语句
  • PooledConnection.close:由连接池管理器调用以关闭由PooledConnection对象提取的物理连接和关联的物理语句

应用程序不能直接关闭正在合并的物理语句; 而是由连接池管理器完成。 PooledConnection.close方法关闭连接,并在给定的连接上打开所有的语句,释放与这些语句关联的资源

应用程序也不直接控制如何汇总语句。 一个语句池与PooledConnection对象相关联,它的行为由生成它的ConnectionPoolDataSource对象的属性决定。 第11.8节“ConnectionPoolDataSource属性”讨论了这些属性

11.7 声明事件

如果连接池管理器支持PreparedStatement对象的Statement pooling,则必须实现StatementEventListener接口,然后由该PooledConnection对象注册为侦听器。 StatementEventListener接口定义了以下两种方法,它们对应于PreparedStatement对象可能发生的两种事件

  • statementClosed :当与此PooledConnection对象关联的逻辑PreparedStatement对象关闭时触发,即应用程序称为方法PreparedStatement.close
  • statementErrorOccurred : 当JDBC驱动程序确定PreparedStatement对象不再有效时触发

连接池管理器使用PooledConnection.addStatementEventListener方法将自己注册为PreparedStatement对象的侦听器。 通常,连接池管理器将自己注册为StatementEventListener,然后将PreparedStatement对象返回给应用程序

当相应的事件发生时,驱动程序调用StatementEventListener方法statementClosed和statementErrorOccurred。 两个方法都将一个statementEvent对象作为参数,可用于确定哪个PreparedStatement对象已关闭或出现错误。 当JDBC应用程序关闭其逻辑准备语句时,JDBC驱动程序通过调用监听器对方法statementClosed的实现来通知连接池管理器(监听器)。 此时,连接池管理器可以将PreparedStatement对象返回到池中以供重用。

当发生使PreparedStatement对象无效的错误时,JDBC驱动程序通过调用其statementErrorOccurred方法通知侦听器,然后将SQLException对象抛出该应用程序以通知其相同的错误

11.8 ConnectionPoolDataSource 属性

属性 类型 描绘
maxStatements int 池应保持打开的语句总数。 0(零)表示缓存语句被禁用
initialPoolSize int 池创建时应包含的物理连接数
minPoolSize int 池应该始终保持可用的物理连接数。 0(零)表示应该根据需要创建连接
maxPoolSize int 池应包含的最大物理连接数。 0(零)表示无最大大小。
maxIdleTime int 在连接关闭之前,物理连接在池中应保持未使用的秒数。 0(零)表示无限制。
propertyCycle int 在执行由上述连接池属性的值定义的当前策略之前,池应等待的时间间隔(秒)

连接池配置属性遵循JavaBeans规范中为JavaBeans组件指定的约定。 连接池供应商可以选择使用实现特定的属性来扩充该集合。 如果是这样,额外的属性必须被赋予不与标准属性名称冲突的名称。

像DataSource实现一样,ConnectionPoolDataSource实现必须为其支持的每个属性提供“getter”和“setter”方法。 通常在部署ConnectionPoolDataSource对象时初始化这些属性。 代码示例11-5说明了供应商实现ConnectionPoolDataSource接口中的设置属性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 本文包括传统JDBC的缺点连接池原理自定义连接池开源数据库连接池DBCP连接池C3P0连接池Tomcat内置连接池...
    廖少少阅读 16,686评论 0 37
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • application的配置属性。 这些属性是否生效取决于对应的组件是否声明为Spring应用程序上下文里的Bea...
    新签名阅读 5,335评论 1 27
  • 春暖花开,万物复苏,小细菌们也猖狂起来,保护好家人,从时刻保护好双手的清洁开始。日本本土狮王儿童洗手液,纯植物配方...
    爱笑的眼睛37阅读 2,205评论 0 50