第十一章 连接池(Connection Pooling)

2017-08-21  本文已影响943人  zlb

在基本的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对象可能发生的两种事件:

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

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

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

11.3 三层环境中的连接池

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

11.4 DataSource实现和连接池

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

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

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

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语句:

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

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

11.7 声明事件

如果连接池管理器支持PreparedStatement对象的Statement pooling,则必须实现StatementEventListener接口,然后由该PooledConnection对象注册为侦听器。 StatementEventListener接口定义了以下两种方法,它们对应于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接口中的设置属性

上一篇下一篇

猜你喜欢

热点阅读