前言

整理这个官方翻译的系列,原因是网上大部分的 tomcat 版本比较旧,此版本为 v11 最新的版本。

开源项目

从零手写实现 tomcat minicat 别称【嗅虎】心有猛虎,轻嗅蔷薇。

系列文章

web server apache tomcat11-01-官方文档入门介绍

web server apache tomcat11-02-setup 启动

web server apache tomcat11-03-deploy 如何部署

web server apache tomcat11-04-manager 如何管理?

web server apache tomcat11-06-Host Manager App – Text Interface

web server apache tomcat11-07-Realm Configuration

web server apache tomcat11-08-JNDI Resources

web server apache tomcat11-09-JNDI Datasource

web server apache tomcat11-10-Class Loader

web server apache tomcat11-11-Jasper 2 JSP Engine

web server apache tomcat11-12-SSL/TLS Configuration

web server apache tomcat11-13-SSI

web server apache tomcat11-14-CGI

web server apache tomcat11-15-proxy

web server apache tomcat11-16-mbean

web server apache tomcat11-17-default-servlet

web server apache tomcat11-18-clusting 集群

web server apache tomcat11-19-load balance 负载均衡

web server apache tomcat11-20-connectors 连接器

web server apache tomcat11-21-monitor and management 监控与管理

web server apache tomcat11-22-logging 日志

web server apache tomcat11-23-APR

web server apache tomcat11-24-Virtual Hosting and Tomcat

web server apache tomcat11-25-Advanced IO and Tomcat

web server apache tomcat11-26-maven jars

web server apache tomcat11-27-Security Considerations

web server apache tomcat11-28-Windows Service

web server apache tomcat11-29-Windows Authentication

web server apache tomcat11-30-The Tomcat JDBC Connection Pool

web server apache tomcat11-31-websocket

web server apache tomcat11-32-rewrite

web server apache tomcat11-33-CDI

web server apache tomcat11-34-Ahead of Time compilation support

简介

JDBC连接池org.apache.tomcat.jdbc.pool是Apache Commons DBCP连接池的替代或替代方案。

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

以下是一些原因:

  • Commons DBCP 1.x是单线程的。为了保证线程安全,Commons在对象分配和对象返回期间短暂锁定整个池。请注意,这不适用于Commons DBCP 2.x。
  • Commons DBCP 1.x可能会很慢。随着逻辑CPU数量的增长和尝试借用或返回对象的并发线程数量的增加,性能会下降。对于高度并发的系统,影响可能会很大。请注意,这不适用于Commons DBCP 2.x。
  • Commons DBCP有60多个类。tomcat-jdbc-pool核心是8个类,因此对未来需求进行修改将需要更少的更改。这就是运行连接池本身所需的全部内容,其他都是附加的。
  • Commons DBCP使用静态接口。这意味着您必须使用给定JRE版本的正确版本,否则可能会看到NoSuchMethodException异常。
  • 当一个连接池可以通过一个更简单的实现来完成时,重写60多个类是不值得的。

Tomcat jdbc池实现了异步检索连接的能力,而无需向库本身添加额外的线程。Tomcat jdbc池是Tomcat模块,它依赖于Tomcat JULI,这是Tomcat中使用的简化日志记录框架。

功能增加

  • 支持高度并发的环境和多核/CPU系统。
  • 接口的动态实现,将为您的运行时环境支持java.sql和javax.sql接口(只要您的JDBC驱动程序也支持相同的接口),即使使用较低版本的JDK进行编译也是如此。
  • 验证间隔 - 我们不必在每次使用连接时都进行验证,我们可以在借用或返回连接时进行验证,但不要比我们可以配置的间隔更频繁。
  • 一次性运行查询,在建立与数据库的连接时仅运行一次的可配置查询。在建立连接期间设置会话设置非常有用。
  • 能够配置自定义拦截器。这允许您编写自定义拦截器来增强功能。您可以使用拦截器来收集查询统计信息,缓存会话状态,失败时重新连接连接,重试查询,缓存查询结果等等。您的选择是无限的,拦截器是动态的,不限于java.sql/javax.sql接口的JDK版本。
  • 高性能 - 我们将在稍后展示一些性能差异
  • 极其简单,由于实现非常简化,所以行数和源文件数非常低,与具有超过200个源文件(上次我们检查时)的c3p0相比,Tomcat jdbc有8个文件的核心,连接池本身约为其一半。由于可能会发生错误,因此跟踪错误将更快,更容易修复。从一开始,简化复杂性就是一个重点。
  • 异步连接检索 - 您可以排队请求连接并收到 Future<Connection>
  • 更好的空闲连接处理。它不直接关闭连接,仍然可以对连接进行池化,并且使用更智能的算法调整空闲池的大小。
  • 您可以在什么时候将连接视为被丢弃时进行决定,是在池满时还是在指定的超时时直接进行,通过指定池使用阈值来实现。
  • 放弃连接定时器将在语句/查询活动时重置。允许长时间使用的连接不超时。这是通过ResetAbandonedTimer实现的
  • 连接在连接一段时间后关闭。基于年龄的返回池后关闭。
  • 当怀疑连接被丢弃时,获得JMX通知和日志条目。这类似于removeAbandonedTimeout,但它不采取任何行动,只报告信息。这是通过suspectTimeout属性实现的。
  • 连接可以从java.sql.Driver、javax.sql.DataSource或javax.sql.XADataSource中检索。这是通过dataSource和dataSourceJNDI属性实现的。

如何使用

Tomcat连接池的使用尽可能简单,对于那些熟悉commons-dbcp的人来说,过渡将非常简单。从其他连接池过渡也相当简单。

其他功能

Tomcat连接池提供了一些其他功能,超出了大多数其他池所能做的:

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

    在Apache Tomcat容器内部

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

独立运行

连接池仅具有另一个依赖项,即tomcat-juli.jar。要在独立项目中使用bean实例化来配置池,要实例化的bean是org.apache.tomcat.jdbc.pool.DataSource。与配置J

NDI资源的连接池相同的属性(下面有文档)被用于配置数据源作为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 类型应始终为 javax.sql.DataSource 或 javax.sql.XADataSource

根据类型,将创建 org.apache.tomcat.jdbc.pool.DataSource 或 org.apache.tomcat.jdbc.pool.XADataSource。

系统属性

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

属性 描述
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader (布尔值) 控制动态类(如 JDBC 驱动程序、拦截器和验证器)的类加载。如果设置为 false(默认值),池将首先尝试使用当前加载器(即加载池类的类加载器),如果类加载失败,则尝试使用线程上下文加载器加载。如果您希望与 Apache Tomcat 8.0.8 及更早版本保持向后兼容,并且仅尝试使用当前加载器,则将此值设置为 true。如果未设置,则默认值为 false。

共同属性

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

属性 描述
defaultAutoCommit (布尔值) 此池创建的连接的默认自动提交状态。如果未设置,则默认为 JDBC 驱动程序的默认值(如果未设置,则不会调用 setAutoCommit 方法)。
defaultReadOnly (布尔值) 此池创建的连接的默认只读状态。如果未设置,则不会调用 setReadOnly 方法。(某些驱动程序不支持只读模式,例如:Informix)
defaultTransactionIsolation (字符串) 此池创建的连接的默认事务隔离状态。以下之一:NONE、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE(请参阅 javadoc)。如果未设置,则不会调用该方法,并默认为 JDBC 驱动程序的默认值。
defaultCatalog (字符串) 此池创建的连接的默认目录。
driverClassName (字符串) 要使用的 JDBC 驱动程序的完全限定的 Java 类名。驱动程序必须从与 tomcat-jdbc.jar 相同的类加载器中访问。
username (字符串) 传递给我们的 JDBC 驱动程序以建立连接的连接用户名。请注意,默认情况下,DataSource.getConnection(username,password) 方法不会使用传递到该方法的凭据,而是使用此处配置的凭据。有关更多详细信息,请参阅 alternateUsernameAllowed 属性。
password (字符串) 传递给我们的 JDBC 驱动程序以建立连接的连接密码。请注意,默认情况下,DataSource.getConnection(username,password) 方法不会使用传递到该方法的凭据,而是使用此处配置的凭据。有关更多详细信息,请参阅 alternateUsernameAllowed 属性。
maxActive (整数) 此池同时可分配的最大活动连接数。默认值为 100。
maxIdle (整数) 应始终保留在池中的最大连接数。默认值为 maxActive:100。空闲连接定期检查(如果启用),并且空闲时间超过 minEvictableIdleTimeMillis 的连接将被释放。(另请参阅 testWhileIdle)
minIdle (整数) 应始终保留在池中的最小已建立连接数。如果验证查询失败,连接池可以缩小到此数字以下。默认值由 initialSize:10 派生。(另请参阅 testWhileIdle)
initialSize (整数) 在启动池时创建的连接的初始数量。默认值为 10。
maxWait (整数) 在没有可用连接时,池将等待连接返回的最大毫秒数,然后抛出异常。默认值为 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 秒)。
minEvictableIdleTimeMillis (整数) 对象在池中闲置的最短时间,然后才能被清除。默认值为 60000(60 秒)。
accessToUnderlyingConnectionAllowed (布尔值) 属性未使用。
可以通过对池化连接调用 unwrap 来实现访问。参见 javax.sql.DataSource 接口,或通过反射调用 getConnection 或将对象强制转换为 javax.sql.PooledConnection。    
  removeAbandoned (布尔值) 如果连接超过 removeAbandonedTimeout,则标志为删除废弃的连接。如果设置为 true,连接被视为废弃并且有资格进行删除,如果它的使用时间超过 removeAbandonedTimeout。将此设置为 true 可从未关闭连接的应用程序中恢复 db 连接。另请参阅 logAbandoned,默认值为 false。
  removeAbandonedTimeout (整数) 废弃的(正在使用的)连接可以被删除之前的超时时间(以秒为单位)。默认值为 60(60 秒)。该值应设置为应用程序可能具有的最长运行查询时间。
  logAbandoned (布尔值) 记录弃用的连接的应用程序代码的堆栈跟踪的标志。记录弃用的连接会增加每个连接的开销,因为必须生成堆栈跟踪。默认值为 false。
  connectionProperties (字符串) 在建立新连接时将发送给我们的 JDBC 驱动程序的连接属性。字符串的格式必须是 [propertyName=property;]* 注 - “user” 和 “password” 属性将被明确传递,因此不需要在此处包括它们。默认值为 null。
  poolPreparedStatements (布尔值) 属性未使用。
  maxOpenPreparedStatements (整数) 属性未使用。

Tomcat JDBC 增强属性

属性 描述
initSQL (字符串) 第一次创建连接时要运行的自定义查询。默认值为 null。
jdbcInterceptors (字符串) 扩展 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 (长整型) 避免过度验证,最多以此频率运行验证 - 毫秒时间。 如果连接应进行验证,但已在此间隔内先前进行过验证,则不会再次进行验证。 默认值为 3000(3 秒)。
jmxEnabled (布尔值) 是否将池注册到 JMX。默认值为 true。
fairQueue (布尔值) 如果希望对 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 (整数) 如果连接已被废弃(超时),除非正在使用的连接数超过 abandonWhenPercentageFull 定义的百分比,否则不会关闭并报告。 值应在 0-100 之间。 默认值为 0,这意味着一旦达到 removeAbandonedTimeout,连接就有资格被关闭。
maxAge (长整型) 在重新创建连接之前保留连接的时间(以毫秒为单位)。 当从池中借用连接时,池将检查 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,则池可以在将连接返回到池时通过在连接上调用 rollback 终止事务。 默认值为 false。
commitOnReturn (布尔值) 如果 autoCommit==false,则池可以在将连接返回到池时通过在连接上调用 commit 完成事务。 如果 rollbackOnReturn==true,则会忽略此属性。 默认值为 false。
alternateUsernameAllowed (布尔值) 默认情况下,jdbc-pool 将忽略 DataSource.getConnection(username,password) 调用,并仅返回在全局配置的属性 username 和 password 下之前池化的连接,出于性能原因。 但是,池可以配置为允许每次请求连接时使用不同的凭据。 要启用 DataSource.getConnection(username,password) 调用中描述的功能,请将属性 alternateUsernameAllowed 设置为 true。 如果请求使用凭据 user1
/password1 连接,但是连接之前使用了不同的 user2/password2 连接,则连接将被关闭,并使用请求的凭据重新打开。 这样,池大小仍然在全局级别上进行管理,而不是在每个模式级别上进行管理。 默认值为 false。 此属性作为增强功能添加到 bug 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。

org.apache.tomcat.jdbc.pool.JdbcInterceptor

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

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

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

缓存以下属性的连接:autoCommit、readOnly、transactionIsolation 和 catalog。这是一种性能增强,可避免在调用 getter 或以已设置值调用 setter 时往返数据库。

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

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

属性 描述
trace (字符串形式的布尔值) 启用未关闭语句的跟踪。启用后,当关闭连接且语句未关闭时,拦截器将记录所有堆栈跟踪。默认值为 false。

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

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

属性 描述
prepared (字符串形式的布尔值) 启用使用 prepareStatement 调用创建的 PreparedStatement 实例的缓存。默认值为 true。
callable (字符串形式的布尔值) 启用使用 prepareCall 调用创建的 CallableStatement 实例的缓存。默认值为 false。
max (字符串形式的整数) 连接池中缓存的语句计数限制。一旦计数达到最大值,后续语句将不会返回到缓存中并立即关闭。默认值为 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 驱动程序仍需执行查询超时。

属性 描述
queryTimeout (字符串形式的整数) 设置查询超时的秒数。值小于等于零将禁用此功能。默认值为 1 秒。

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

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

| 属性 | 描述 | | ———– | —————————————————————————————————————————————————————————————————————————————————————————————— | | threshold | (字符串形式的整数) 查询必须超过的毫秒数才能发出日志警报。默认值为 1000 毫秒。 | | maxQueries | (字符串形式的整数) 为了保留内存空间,要跟踪的最大查询数。小于等于 0 的值将禁用此功能。默认值为 1000。 | | logSlow | (字符串形式的布尔值) 如果希望记录慢查询,则设置为 true。默认值为 true。 | | logFailed | (字符串形式的布尔值) 如果希望记录失败的查询,则设置为 true。默认值为 false。 |

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

扩展了 SlowQueryReport,并且除了发出日志条目外,还向监控工具发出 JMX 通知。继承了其父类的所有属性。此类使用 Tomcat 的 JMX 引擎,因此在 Tomcat 容器外部不起作用。默认情况下,如果启用了 ConnectionPool mbean,则 JMX 通知将通过该 mbean 发送。如果 notifyPool=false,则 SlowQueryReportJmx 还可以注册一个 MBean。

属性 描述
notifyPool (字符串形式的布尔值) 如果希望 JMX 通知发送到 SlowQueryReportJmx MBean,则设置为 false。默认值为 true。
objectName (字符串) 定义一个有效的 javax.management.ObjectName 字符串,用于将此对象注册到平台 mbean 服务器。默认值为 null,对象将使用 tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=pool-name 注册。

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

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

代码例子

 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://localhost: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) {}
          }
      }

  }

AS as resource

<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://localhost: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("连接尚未可用。执行一些后台工作");
        try {
            Thread.sleep(100); // 模拟工作
        } catch (InterruptedException x) {
            Thread.currentThread().interrupt();
        }
    }
    con = future.get(); // 应立即返回
    Statement st = con.createStatement();
    ResultSet rs = st.executeQuery("SELECT * FROM user");
} catch (SQLException e) {
    e.printStackTrace();
}

拦截器

拦截器是一种强大的方法,可在特定连接或其子组件上启用、禁用或修改功能。拦截器有许多不同的使用情况。出于性能原因,默认情况下,连接池是无状态的。池本身插入的唯一状态是 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() 方法的调用在连接已关闭时成为 no-op:

if ("close".equals(method.getName())) {
    if (isClosed()) return null; // 对已关闭的连接进行 no-op
}
return super.invoke(proxy, method, args);

如果拦截器具有 useEquals=true 标志,则 compare(String, Method) 方法将使用该标志,并在 method 名称是字符串值时进行字符串值比较。如果使用的是 useEquals=true 标志,则 compare(String, Method) 方法将使用该标志,并在 method 名称是字符串值时进行字符串值比较。

池的启动/停止

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

public void poolStarted(ConnectionPool pool) {
}

public void poolClosed(ConnectionPool pool) {
}

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

配置拦截器

使用 jdbcInterceptors 属性或 setJdbcInterceptors 方法配置拦截器。拦截器可以具有属性,并且配置如下:

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

拦截器属性

由于拦截器可以具有属性,因此您需要能够在拦截器内部读取这些属性的值。例如,可以重写 setProperties(Map<String, InterceptorProperty> properties) 方法。

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 及更高版本。

可以在 Tomcat 文档中找到 JDBC 使用的其他示例。

从源代码构建

构建非常简单。池依赖于 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  (下载依赖项)
ant build     (编译并生成 .jar 文件)
ant

 dist      (创建一个发布包)
ant test      (运行测试,需要设置测试数据库)

系统结构设计为 Maven 构建,但会生成发行版工件。只是库本身。

参考资料

https://tomcat.apache.org/tomcat-11.0-doc/jdbc-pool.html