Tomcat JDBC 连接池

目录

简介

JDBC 连接池 org.apache.tomcat.jdbc.poolApache Commons DBCP 连接池的替代品或替代方案。

那么,为什么我们需要一个新的连接池呢?

以下是一些原因:

  1. Commons DBCP 1.x 是单线程的。为了保证线程安全,Commons 在对象分配和对象返回期间都会对整个池进行短暂的锁定。请注意,这并不适用于 Commons DBCP 2.x。
  2. Commons DBCP 1.x 可能会很慢。随着逻辑 CPU 数量的增加以及尝试借用或返回对象的并发线程数量的增加,性能会下降。对于高并发系统,影响可能是显著的。请注意,这并不适用于 Commons DBCP 2.x。
  3. Commons DBCP 包含超过 60 个类。tomcat-jdbc-pool 的核心只有 8 个类,因此未来需求的修改将需要更少的更改。这些是运行连接池本身所需的一切,其余都是锦上添花。
  4. Commons DBCP 使用静态接口。这意味着您必须使用特定 JRE 版本的正确版本,否则您可能会看到 NoSuchMethodException 异常。
  5. 重写超过 60 个类并不值得,因为连接池可以通过更简单的实现来完成。
  6. Tomcat jdbc 池实现了异步获取连接的能力,而无需在库本身中添加额外的线程。
  7. Tomcat jdbc 池是 Tomcat 的一个模块,它依赖于 Tomcat JULI,一个在 Tomcat 中使用的简化日志框架。
  8. 使用 javax.sql.PooledConnection 接口获取底层连接。
  9. 防止饥饿。如果池为空,并且线程正在等待连接,当连接返回时,池将唤醒正在等待的正确线程。大多数池只会简单地饿死。

与其他连接池实现相比新增的功能

  1. 支持高并发环境和多核/多 CPU 系统。
  2. 接口的动态实现,将支持 java.sqljavax.sql 接口以适应您的运行时环境(只要您的 JDBC 驱动程序也这样做),即使是在较低版本的 JDK 上编译也是如此。
  3. 验证间隔 - 我们不必每次使用连接时都进行验证,我们可以在借用或返回连接时进行验证,只是不要比我们可以配置的间隔更频繁。
  4. 运行一次查询,一个可配置的查询,只会在建立与数据库的连接时运行一次。非常有用,可以设置您希望在连接建立期间一直存在的会话设置。
  5. 能够配置自定义拦截器。这允许您编写自定义拦截器来增强功能。您可以使用拦截器来收集查询统计信息、缓存会话状态、在失败时重新连接连接、重试查询、缓存查询结果等等。您的选择是无限的,拦截器是动态的,不绑定到 java.sql/javax.sql 接口的 JDK 版本。
  6. 高性能 - 我们将在后面展示一些性能差异
  7. 极其简单,由于实现非常简单,代码行数和源文件数量非常少,与 c3p0(上次我们检查时有超过 200 个源文件)相比,Tomcat jdbc 的核心只有 8 个文件,连接池本身大约是它的二分之一。由于可能出现错误,它们将更容易追踪,也更容易修复。从一开始,复杂性降低就是一个重点。
  8. 异步连接获取 - 您可以将您的连接请求排队,并接收一个 Future<Connection> 回来。
  9. 更好的空闲连接处理。它不会直接关闭连接,而是可以继续池化连接,并使用更智能的算法来调整空闲池的大小。
  10. 您可以决定何时将连接视为已放弃,是在池满时,还是直接在指定池使用阈值的超时时。
  11. 当执行语句/查询操作时,放弃连接计时器将重置。允许长时间使用的连接不会超时。这是通过使用 `ResetAbandonedTimer` 实现的。
  12. 在连接建立一段时间后关闭连接。基于年龄的关闭在返回池时进行。
  13. 当怀疑连接被遗弃时,获取 JMX 通知和日志条目。这类似于 `removeAbandonedTimeout`,但它不采取任何行动,只报告信息。这是通过使用 `suspectTimeout` 属性实现的。
  14. 可以从 `java.sql.Driver`、`javax.sql.DataSource` 或 `javax.sql.XADataSource` 中检索连接。这是通过使用 `dataSource` 和 `dataSourceJNDI` 属性实现的。
  15. XA 连接支持

如何使用

Tomcat 连接池的使用尽可能简单,对于熟悉 commons-dbcp 的用户来说,过渡非常简单。从其他连接池迁移也很直接。

附加功能

Tomcat 连接池提供了一些比大多数其他池提供的功能。

  • initSQL - 在连接创建时,能够精确地执行一次 SQL 语句。
  • validationInterval - 除了对连接进行验证之外,还可以避免过于频繁地运行验证。
  • jdbcInterceptors - 灵活且可插拔的拦截器,用于围绕池、查询执行和结果集处理创建任何自定义。有关详细信息,请参阅高级部分。
  • fairQueue - 将公平标志设置为 true 以实现线程公平性或使用异步连接检索。

Apache Tomcat 容器内部

Tomcat 连接池被配置为一个资源,如 Tomcat JDBC 文档 中所述。唯一的区别是您必须指定 `factory` 属性并将值设置为 `org.apache.tomcat.jdbc.pool.DataSourceFactory`。

独立

连接池只有一个依赖项,即 tomcat-juli.jar。要使用 bean 实例化在独立项目中配置池,要实例化的 bean 是 `org.apache.tomcat.jdbc.pool.DataSource`。与您用于将连接池配置为 JNDI 资源相同的属性(在下面有记录)用于将数据源配置为 bean。

JMX

连接池对象公开了一个可以注册的 MBean。为了让连接池对象创建 MBean,标志jmxEnabled必须设置为 true。这并不意味着池将注册到 MBean 服务器,而仅仅是 MBean 被创建。在像 Tomcat 这样的容器中,Tomcat 本身会将 DataSource 注册到 MBean 服务器,org.apache.tomcat.jdbc.pool.DataSource对象将随后注册实际的连接池 MBean。如果您在容器外部运行,您可以自己将 DataSource 注册到您指定的任何对象名称下,它会将注册传播到底层池。为此,您需要调用mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)。在此调用之前,请确保通过调用dataSource.createPool()创建了池。

属性

为了提供一个非常简单的 commons-dbcp 和 tomcat-jdbc-pool 之间的切换,大多数属性是相同的,并且具有相同的含义。

JNDI 工厂和类型

属性 描述
factory

factory 是必需的,并且值应为org.apache.tomcat.jdbc.pool.DataSourceFactory

type

Type 应始终为javax.sql.DataSourcejavax.sql.XADataSource

根据类型,将创建org.apache.tomcat.jdbc.pool.DataSourceorg.apache.tomcat.jdbc.pool.XADataSource

系统属性

系统属性是 JVM 范围的,影响 JVM 中创建的所有池

属性 描述
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader

(boolean) 控制动态类的类加载,例如 JDBC 驱动程序、拦截器和验证器。如果设置为false(默认值),池将首先尝试使用当前加载器(即加载池类的类加载器)进行加载,如果类加载失败,则尝试使用线程上下文加载器进行加载。如果希望与 Apache Tomcat 8.0.8 及更早版本保持向后兼容,并将此值设置为true,并且只尝试当前加载器。如果未设置,则默认值为false

通用属性

这些属性在 commons-dbcp 和 tomcat-jdbc-pool 之间共享,在某些情况下,默认值不同。

属性 描述
defaultAutoCommit

(boolean) 此池创建的连接的默认自动提交状态。如果未设置,则默认为 JDBC 驱动程序默认值(如果未设置,则不会调用setAutoCommit方法)。

defaultReadOnly

(boolean) 此池创建的连接的默认只读状态。如果未设置,则不会调用setReadOnly方法。(某些驱动程序不支持只读模式,例如:Informix)

defaultTransactionIsolation

(字符串) 此池创建的连接的默认事务隔离状态。以下之一:(参见 javadoc)

  • NONE
  • READ_COMMITTED
  • READ_UNCOMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

如果未设置,则不会调用该方法,它默认为 JDBC 驱动程序。

defaultCatalog

(字符串) 此池创建的连接的默认目录。

driverClassName

(字符串) 要使用的 JDBC 驱动的完全限定 Java 类名。驱动程序必须与 tomcat-jdbc.jar 相同的类加载器访问。

username

(字符串) 要传递给我们的 JDBC 驱动程序以建立连接的连接用户名。请注意,默认情况下,方法 DataSource.getConnection(username,password) 不会使用传递到方法中的凭据,而是使用此处配置的凭据。有关更多详细信息,请参见 alternateUsernameAllowed 属性。

password

(字符串) 要传递给我们的 JDBC 驱动程序以建立连接的连接密码。请注意,默认情况下,方法 DataSource.getConnection(username,password) 不会使用传递到方法中的凭据,而是使用此处配置的凭据。有关更多详细信息,请参见 alternateUsernameAllowed 属性。

maxActive

(int) 可以同时从此池分配的最大活动连接数。默认值为 100

maxIdle

(int) 始终应保留在池中的最大连接数。默认值为 maxActive:100 空闲连接会定期检查(如果已启用),并且空闲时间超过 minEvictableIdleTimeMillis 的连接将被释放。(另请参见 testWhileIdle

minIdle

(int) 始终应保留在池中的最小已建立连接数。如果验证查询失败,连接池可以缩小到此数字以下。默认值从 initialSize:10 派生(另请参见 testWhileIdle

initialSize

(int) 池启动时创建的初始连接数。默认值为 10

maxWait

(int) 池在抛出异常之前等待连接返回(当没有可用连接时)的最大毫秒数。默认值为 30000(30 秒)

testOnBorrow

(布尔值) 指示是否在从池中借用对象之前验证对象。如果对象验证失败,它将从池中删除,我们将尝试借用另一个对象。为了更有效地验证,请参见 validationInterval。默认值为 false

testOnConnect

(布尔值) 指示是否在首次创建连接时验证对象。如果对象验证失败,它将抛出 SQLException。默认值为 false

testOnReturn

(布尔值) 指示是否在将对象返回到池之前验证对象。默认值为 false

testWhileIdle

(布尔值) 指示是否由空闲对象逐出器(如果有)验证对象。如果对象验证失败,它将从池中删除。默认值为 false,并且必须设置此属性才能运行池清理器/测试线程(另请参见 timeBetweenEvictionRunsMillis

validationQuery

(字符串) 将用于验证来自此池的连接(在将它们返回给调用者之前)的 SQL 查询。如果指定,此查询不必返回任何数据,它只是不能抛出 SQLException。默认值为 null。如果未指定,连接将通过 isValid() 方法进行验证。示例值为 SELECT 1(mysql)、select 1 from dual(oracle)、SELECT 1(MS Sql Server)

validationQueryTimeout

(整数) 连接验证查询失败之前的超时时间(以秒为单位)。这是通过在执行 validationQuery 的语句上调用 java.sql.Statement.setQueryTimeout(seconds) 来实现的。池本身不会超时查询,它仍然取决于 JDBC 驱动程序来强制执行查询超时。小于或等于零的值将禁用此功能。默认值为 -1

validatorClassName

(字符串) 实现 org.apache.tomcat.jdbc.pool.Validator 接口并提供无参数构造函数(可能是隐式的)的类的名称。如果指定,该类将用于创建 Validator 实例,然后使用它而不是任何验证查询来验证连接。默认值为 null。示例值为 com.mycompany.project.SimpleValidator

timeBetweenEvictionRunsMillis

(整数) 空闲连接验证/清理线程运行之间要休眠的毫秒数。此值不应设置为低于 1 秒。它决定我们检查空闲、放弃的连接的频率,以及我们验证空闲连接的频率。如果后者非零且更低,此值将被 maxAge 覆盖。默认值为 5000(5 秒)。

numTestsPerEvictionRun

(int) tomcat-jdbc-pool 中未使用此属性。

minEvictableIdleTimeMillis

(int) 对象在池中处于空闲状态的最小时间,在此时间之后,该对象才有资格被驱逐。默认值为 60000(60 秒)。

accessToUnderlyingConnectionAllowed

(boolean) 未使用的属性。可以通过在池化连接上调用 unwrap 来实现访问。请参阅 javax.sql.DataSource 接口,或通过反射调用 getConnection,或将对象强制转换为 javax.sql.PooledConnection

removeAbandoned

(boolean) 如果连接超过 removeAbandonedTimeout,则将其删除。如果设置为 true,则如果连接的使用时间超过 removeAbandonedTimeout,则该连接将被视为已放弃并有资格被删除。将此设置为 true 可以从无法关闭连接的应用程序中恢复数据库连接。另请参阅 logAbandoned。默认值为 false

removeAbandonedTimeout

(int) 在可以删除已放弃(正在使用)的连接之前的时间(以秒为单位)。默认值为 60(60 秒)。该值应设置为应用程序可能具有的最长运行查询。

logAbandoned

(boolean) 标志,用于记录放弃连接的应用程序代码的堆栈跟踪。记录已放弃的连接会为每次连接借用增加开销,因为必须生成堆栈跟踪。默认值为 false

connectionProperties

(String) 在建立新连接时将发送到我们的 JDBC 驱动程序的连接属性。字符串的格式必须为 [propertyName=property;]* 注意 - "user" 和 "password" 属性将被显式传递,因此无需在此处包含它们。默认值为 null

poolPreparedStatements

(boolean) 未使用的属性。

maxOpenPreparedStatements

(int) 未使用的属性。

Tomcat JDBC 增强属性

属性 描述
initSQL

(String) 首次创建连接时要运行的自定义查询。默认值为 null

jdbcInterceptors

(String) 以分号分隔的扩展 org.apache.tomcat.jdbc.pool.JdbcInterceptor 类的类名列表。有关语法和示例的更详细说明,请参阅下面的 配置 JDBC 拦截器

这些拦截器将作为拦截器插入到java.sql.Connection对象的操作链中。默认值为null

预定义拦截器
org.apache.tomcat.jdbc.pool.interceptor.
ConnectionState
- 跟踪自动提交、只读、目录和事务隔离级别。
org.apache.tomcat.jdbc.pool.interceptor.
StatementFinalizer
- 跟踪打开的语句,并在连接返回到池时关闭它们。

更多预定义拦截器在JDBC 拦截器部分中详细描述。

validationInterval

(long) 避免过度验证,仅以该频率(以毫秒为单位)运行验证。如果连接需要验证,但在此间隔内之前已验证过,则不会再次验证。默认值为3000(3 秒)。

jmxEnabled

(boolean) 是否将池注册到 JMX。默认值为true

fairQueue

(boolean) 如果希望对 getConnection 的调用以真正的 FIFO 方式公平对待,则将其设置为 true。这将使用org.apache.tomcat.jdbc.pool.FairBlockingQueue实现来表示空闲连接列表。默认值为true。当您想使用异步连接检索时,需要此标志。
设置此标志可确保线程按到达顺序接收连接。
在性能测试期间,锁和锁等待的实现方式存在很大差异。当fairQueue=true时,会根据系统运行的操作系统做出决策。如果系统在 Linux 上运行(属性os.name=Linux。要禁用此 Linux 特定行为并继续使用公平队列,只需在加载连接池类之前将属性org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true添加到系统属性中。

abandonWhenPercentageFull

(int) 只有当正在使用的连接数量超过abandonWhenPercentageFull定义的百分比时,才会关闭并报告已放弃(超时)的连接。该值应介于 0-100 之间。默认值为0,这意味着连接在达到removeAbandonedTimeout后即可关闭。

maxAge

(long) 保留连接的时间(以毫秒为单位),然后重新创建它。当从池中借用连接时,池将检查是否已达到now - time-when-connected > maxAge,如果是,则在借用之前重新连接。当连接返回到池中时,池将检查是否已达到now - time-when-connected > maxAge,如果是,则尝试重新连接。当连接处于空闲状态且timeBetweenEvictionRunsMillis大于零时,池将定期检查是否已达到now - time-when-connected > maxAge,如果是,则尝试重新连接。将maxAge设置为低于timeBetweenEvictionRunsMillis的值将覆盖它(因此空闲连接验证/清理将更频繁地运行)。默认值为0,这意味着连接将保持打开状态,并且在从池中借用、将连接返回到池中或检查空闲连接时不会进行年龄检查。

useEquals

(布尔值) 如果希望 ProxyConnection 类使用 String.equals 进行比较,则设置为 true;如果希望使用 == 进行比较,则设置为 false。此属性不适用于添加的拦截器,因为这些拦截器是单独配置的。默认值为 true

suspectTimeout

(整数) 超时值,以秒为单位。默认值为 0
类似于 removeAbandonedTimeout 值,但不会将连接视为已放弃并可能关闭连接,而是在 logAbandoned 设置为 true 时仅记录警告。如果此值等于或小于 0,则不会执行可疑检查。可疑检查仅在超时值大于 0 且连接未被放弃或放弃检查被禁用时才会执行。如果连接可疑,则会记录一条 WARN 消息,并且会发送一次 JMX 通知。

rollbackOnReturn

(布尔值) 如果 autoCommit==false,则池可以在将连接返回到池时通过对连接调用回滚来终止事务。默认值为 false

commitOnReturn

(布尔值) 如果 autoCommit==false,则池可以在将连接返回到池时通过对连接调用提交来完成事务。如果 rollbackOnReturn==true,则忽略此属性。默认值为 false

alternateUsernameAllowed

(布尔值) 默认情况下,jdbc-pool 会忽略 DataSource.getConnection(username,password) 调用,并仅返回在全局配置的属性 usernamepassword 下先前池化的连接,以提高性能。

但是,可以配置池以允许每次请求连接时使用不同的凭据。要启用 DataSource.getConnection(username,password) 调用中描述的功能,只需将属性 alternateUsernameAllowed 设置为 true
如果您使用凭据 user1/password1 请求连接,而该连接先前使用不同的 user2/password2 连接,则该连接将被关闭,并使用请求的凭据重新打开。这样,池大小仍然在全局级别管理,而不是在每个模式级别管理。
默认值为 false
此属性是作为对 错误 50025 的增强而添加的。

dataSource

(javax.sql.DataSource) 将数据源注入连接池,池将使用数据源检索连接,而不是使用 java.sql.Driver 接口建立连接。当您希望池化 XA 连接或使用数据源而不是连接字符串建立的连接时,这很有用。默认值为 null

dataSourceJNDI

(字符串) 要在 JNDI 中查找的数据源的 JNDI 名称,然后使用它建立与数据库的连接。请参见 dataSource 属性。默认值为 null

useDisposableConnectionFacade

(布尔值) 如果您希望在连接上放置一个外观,以便在连接关闭后无法重用它,请将其设置为 true。这可以防止线程持有对它已经调用过关闭的连接的引用,以在其上执行查询。默认值为 true

logValidationErrors

(布尔值) 将此设置为 true 以将验证阶段期间的错误记录到日志文件。如果设置为 true,错误将被记录为 SEVERE。默认值为 false,以保持向后兼容性。

propagateInterruptState

(布尔值) 将此设置为 true 以传播已中断线程的中断状态(不清除中断状态)。默认值为 false,以保持向后兼容性。

ignoreExceptionOnPreLoad

(布尔值) 标志是否忽略在初始化池时创建连接的错误。如果要忽略在初始化池时创建连接的错误,请将其设置为 true。如果要通过抛出异常来使池的初始化失败,请将其设置为 false。默认值为 false

useStatementFacade

(布尔值) 如果您希望包装语句以启用在关闭的语句上调用 equals()hashCode() 方法(如果设置了任何语句代理),请将其设置为 true。默认值为 true

高级用法

JDBC 拦截器

要查看如何使用拦截器的示例,请查看 org.apache.tomcat.jdbc.pool.interceptor.ConnectionState。这个简单的拦截器是三个属性的缓存,事务隔离级别、自动提交和只读状态,以便系统可以避免不必要的数据库往返。

随着需要出现,将向池的核心添加更多拦截器。欢迎贡献!

拦截器当然不仅限于 java.sql.Connection,还可以用于包装来自方法调用的任何结果。您可以构建查询性能分析器,当查询运行时间超过预期时间时提供 JMX 通知。

配置 JDBC 拦截器

配置 JDBC 拦截器是使用 jdbcInterceptors 属性完成的。该属性包含一个以分号分隔的类名列表。如果类名不是完全限定的,它将以 org.apache.tomcat.jdbc.pool.interceptor. 前缀为前缀。

示例
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
等同于
jdbcInterceptors="ConnectionState;StatementFinalizer"

拦截器也可以拥有属性。拦截器的属性在类名后用括号指定。多个属性用逗号分隔。

示例
jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)"

类名、属性名和属性值周围的额外空格字符将被忽略。

org.apache.tomcat.jdbc.pool.JdbcInterceptor

所有拦截器的抽象基类,不能实例化。

属性 描述
useEquals

(布尔值) 如果希望 ProxyConnection 类使用 String.equals,则设置为 true;如果希望在比较方法名时使用 ==,则设置为 false。默认值为 true

org.apache.tomcat.jdbc.pool.interceptor.ConnectionState

缓存连接的以下属性:autoCommitreadOnlytransactionIsolationcatalog。这是一种性能增强,可以避免在调用 getter 或使用已设置的值调用 setter 时进行往返数据库操作。

属性 描述

org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer

跟踪使用 createStatementprepareStatementprepareCall 创建的所有语句,并在连接返回到池时关闭这些语句。

属性 描述
跟踪

(布尔值作为字符串) 启用对未关闭语句的跟踪。启用后,如果连接关闭且语句未关闭,拦截器将记录所有堆栈跟踪。默认值为 false

org.apache.tomcat.jdbc.pool.interceptor.StatementCache

缓存 PreparedStatement 和/或 CallableStatement 实例到连接上。

语句在每个连接上缓存。计数限制对属于同一池的所有连接全局计数。一旦计数达到 max,后续语句将不会返回到缓存,而是立即关闭。

属性 描述
准备好的

(布尔值作为字符串) 启用对使用 prepareStatement 调用创建的 PreparedStatement 实例的缓存。默认值为 true

可调用的

(布尔值作为字符串) 启用对使用 prepareCall 调用创建的 CallableStatement 实例的缓存。默认值为 false

最大值

(整数作为字符串) 连接池中缓存语句数量的限制。默认值为 50

org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor

参见 48392。拦截器用于包装语句和结果集,以防止使用 ResultSet.getStatement().getConnection()Statement.getConnection() 方法访问实际连接。

属性 描述

org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor

在创建新语句时自动调用 java.sql.Statement.setQueryTimeout(seconds)。池本身不会超时查询,它仍然取决于 JDBC 驱动程序来强制执行查询超时。

属性 描述
查询超时

(int 作为字符串) 设置查询超时时间的秒数。小于或等于零的值将禁用此功能。默认值为 1 秒。

org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport

跟踪查询性能,并在查询超过时间阈值或失败时发出日志条目。使用的日志级别为 WARN

属性 描述
阈值

(int 作为字符串) 查询必须超过的毫秒数,才会发出日志警报。默认值为 1000 毫秒。

最大查询数

(int 作为字符串) 为保留内存空间而跟踪的查询最大数量。小于或等于 0 的值将禁用此功能。默认值为 1000

记录慢查询

(boolean 作为字符串) 如果您希望记录慢速查询,请将其设置为 true。默认值为 true

记录失败查询

(boolean 作为字符串) 如果您希望记录失败的查询,请将其设置为 true。默认值为 false

org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx

扩展 SlowQueryReport,除了日志条目之外,它还会为监控工具发出 JMX 通知以做出反应。继承其父类中的所有属性。此类使用 Tomcat 的 JMX 引擎,因此它在 Tomcat 容器之外无法工作。默认情况下,如果启用了 ConnectionPool mbean,则 JMX 通知将通过它发送。如果 notifyPool=falseSlowQueryReportJmx 也可以注册一个 MBean

属性 描述
通知池

(boolean 作为字符串) 如果您希望 JMX 通知发送到 SlowQueryReportJmx MBean,请将其设置为 false。默认值为 true

对象名称

(String) 定义一个有效的 javax.management.ObjectName 字符串,该字符串将用于将此对象注册到平台 mbean 服务器。默认值为 null,并且该对象将使用 tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool 注册

org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer

放弃计时器在从池中检出连接时启动。这意味着如果您有 30 秒的超时时间,并且使用连接运行 10 次 10 秒的查询,它将被标记为放弃,并且可能会根据 abandonWhenPercentageFull 属性被回收。使用此拦截器,它将在您对连接执行操作或成功执行查询时重置检出计时器。

属性 描述

代码示例

有关 JDBC 使用的 Tomcat 配置的其他示例,请参阅 Tomcat 文档

普通 Java

这是一个关于如何创建和使用数据源的简单示例。

  import java.sql.Connection;
  import java.sql.ResultSet;
  import java.sql.Statement;

  import org.apache.tomcat.jdbc.pool.DataSource;
  import org.apache.tomcat.jdbc.pool.PoolProperties;

  public class SimplePOJOExample {

      public static void main(String[] args) throws Exception {
          PoolProperties p = new PoolProperties();
          p.setUrl("jdbc:mysql://127.0.0.1:3306/mysql");
          p.setDriverClassName("com.mysql.jdbc.Driver");
          p.setUsername("root");
          p.setPassword("password");
          p.setJmxEnabled(true);
          p.setTestWhileIdle(false);
          p.setTestOnBorrow(true);
          p.setValidationQuery("SELECT 1");
          p.setTestOnReturn(false);
          p.setValidationInterval(30000);
          p.setTimeBetweenEvictionRunsMillis(30000);
          p.setMaxActive(100);
          p.setInitialSize(10);
          p.setMaxWait(10000);
          p.setRemoveAbandonedTimeout(60);
          p.setMinEvictableIdleTimeMillis(30000);
          p.setMinIdle(10);
          p.setLogAbandoned(true);
          p.setRemoveAbandoned(true);
          p.setJdbcInterceptors(
            "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
            "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
          DataSource datasource = new DataSource();
          datasource.setPoolProperties(p);

          Connection con = null;
          try {
            con = datasource.getConnection();
            Statement st = con.createStatement();
            ResultSet rs = st.executeQuery("select * from user");
            int cnt = 1;
            while (rs.next()) {
                System.out.println((cnt++)+". Host:" +rs.getString("Host")+
                  " User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
            }
            rs.close();
            st.close();
          } finally {
            if (con!=null) try {con.close();}catch (Exception ignore) {}
          }
      }

  }

作为资源

这是一个关于如何配置用于 JNDI 查找的资源的示例

<Resource name="jdbc/TestDB"
          auth="Container"
          type="javax.sql.DataSource"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          testWhileIdle="true"
          testOnBorrow="true"
          testOnReturn="false"
          validationQuery="SELECT 1"
          validationInterval="30000"
          timeBetweenEvictionRunsMillis="30000"
          maxActive="100"
          minIdle="10"
          maxWait="10000"
          initialSize="10"
          removeAbandonedTimeout="60"
          removeAbandoned="true"
          logAbandoned="true"
          minEvictableIdleTimeMillis="30000"
          jmxEnabled="true"
          jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
            org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
          username="root"
          password="password"
          driverClassName="com.mysql.jdbc.Driver"
          url="jdbc:mysql://127.0.0.1:3306/mysql"/>

异步连接检索

Tomcat JDBC 连接池支持异步连接检索,而无需向池库添加额外的线程。它通过向数据源添加一个名为 Future<Connection> getConnectionAsync() 的方法来实现。为了使用异步检索,必须满足两个条件

  1. 您必须将fairQueue属性配置为true
  2. 您需要将数据源强制转换为org.apache.tomcat.jdbc.pool.DataSource
下面展示了使用异步功能的示例。
  Connection con = null;
  try {
    Future<Connection> future = datasource.getConnectionAsync();
    while (!future.isDone()) {
      System.out.println("Connection is not yet available. Do some background work");
      try {
        Thread.sleep(100); //simulate work
      }catch (InterruptedException x) {
        Thread.currentThread().interrupt();
      }
    }
    con = future.get(); //should return instantly
    Statement st = con.createStatement();
    ResultSet rs = st.executeQuery("select * from user");

拦截器

拦截器是一种强大的方式,可以启用、禁用或修改特定连接或其子组件的功能。拦截器在许多不同的用例中都很有用。默认情况下,出于性能原因,连接池是无状态的。池本身插入的唯一状态是defaultAutoCommitdefaultReadOnlydefaultTransactionIsolationdefaultCatalog(如果设置了这些状态)。这 4 个属性仅在连接创建时设置。如果在使用连接期间修改了这些属性,池本身不会重置它们。

拦截器必须扩展org.apache.tomcat.jdbc.pool.JdbcInterceptor类。这个类很简单,您需要有一个无参数构造函数。

  public JdbcInterceptor() {
  }

当从池中借用连接时,拦截器可以通过实现

  public abstract void reset(ConnectionPool parent, PooledConnection con);

方法来初始化或以其他方式对事件做出反应。此方法使用两个参数调用:对连接池本身的引用ConnectionPool parent和对底层连接的引用PooledConnection con

当在java.sql.Connection对象上调用方法时,它将导致

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

方法被调用。Method method是实际调用的方法,Object[] args是参数。为了查看一个非常简单的示例,我们演示了如何在连接已关闭的情况下如何使对java.sql.Connection.close()的调用成为无操作。

  if (CLOSE_VAL==method.getName()) {
      if (isClosed()) return null; //noop for already closed.
  }
  return super.invoke(proxy,method,args);

正在进行观察。它是对方法名称的比较。一种方法是执行"close".equals(method.getName())。在上面,我们看到了方法名称和static final String引用之间的直接引用比较。根据 JVM 规范,方法名称和静态最终字符串最终会进入共享常量池,因此引用比较应该有效。当然也可以这样做

  if (compare(CLOSE_VAL,method)) {
      if (isClosed()) return null; //noop for already closed.
  }
  return super.invoke(proxy,method,args);

compare(String,Method)将使用拦截器上的useEquals标志,并在useEquals=true标志设置时执行引用比较或字符串值比较。

池启动/停止
当连接池启动或关闭时,您可以收到通知。即使它是一个实例方法,您也只会收到每个拦截器类的通知一次,并且您将使用当前未附加到池的拦截器收到通知。

  public void poolStarted(ConnectionPool pool) {
  }

  public void poolClosed(ConnectionPool pool) {
  }

覆盖这些方法时,如果您要扩展除JdbcInterceptor之外的类,请不要忘记调用 super。

配置拦截器
拦截器使用 `jdbcInterceptors` 属性或 `setJdbcInterceptors` 方法进行配置。拦截器可以拥有属性,配置方式如下:

  String jdbcInterceptors=
    "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"

拦截器属性
由于拦截器可以拥有属性,因此您需要能够在拦截器内部读取这些属性的值。以上面的例子为例,您可以重写 `setProperties` 方法。

  public void setProperties(Map<String, InterceptorProperty> properties) {
     super.setProperties(properties);
     final String myprop = "myprop";
     InterceptorProperty p1 = properties.get(myprop);
     if (p1!=null) {
         setMyprop(Long.parseLong(p1.getValue()));
     }
  }

获取实际的 JDBC 连接

连接池在实际连接周围创建包装器,以便正确地池化它们。我们还在这些包装器中创建拦截器,以便执行某些功能。如果需要检索实际连接,可以使用 `javax.sql.PooledConnection` 接口。

  Connection con = datasource.getConnection();
  Connection actual = ((javax.sql.PooledConnection)con).getConnection();

构建

我们使用 1.6 版本构建 JDBC 池代码,但它向后兼容到 1.5 版本的运行时环境。对于单元测试,我们使用 1.6 及更高版本。

有关 JDBC 使用的 Tomcat 配置的其他示例,请参阅 Tomcat 文档

从源代码构建

构建非常简单。池依赖于 `tomcat-juli.jar`,如果您需要 `SlowQueryReportJmx`,则需要添加它。

  javac -classpath tomcat-juli.jar \
        -d . \
        org/apache/tomcat/jdbc/pool/*.java \
        org/apache/tomcat/jdbc/pool/interceptor/*.java \
        org/apache/tomcat/jdbc/pool/jmx/*.java

可以在 Tomcat 的 源代码库 中找到构建文件。

为了方便起见,还包含一个构建文件,其中一个简单的构建命令将生成所有需要的文件。

  ant download  (downloads dependencies)
  ant build     (compiles and generates .jar files)
  ant dist      (creates a release package)
  ant test      (runs tests, expects a test database to be setup)

该系统结构用于 Maven 构建,但会生成发布工件。仅限库本身。