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. 極為簡單,由於簡化的實作,程式碼行數和原始檔數量非常少,與有超過 200 個原始檔的 c3p0 相比(上次我們檢查時),Tomcat jdbc 的核心有 8 個檔案,連線池本身約為其一半。如果發生錯誤,它們會更快被追蹤,也更容易修復。從一開始,複雜度降低就是重點。
  8. 非同步連線擷取 - 您可以將連線要求排隊,並收到 Future<Connection> 回傳。
  9. 更好的閒置連線處理。它不會直接關閉連線,而是可以繼續集中連線,並使用更聰明的演算法調整閒置池的大小。
  10. 您可以決定在什麼時候將連線視為已放棄,是在池滿時,還是直接在逾時時,方法是指定池使用率閾值。
  11. 放棄連線計時器會在陳述式/查詢活動時重設。允許長時間使用的連線不會逾時。這是使用 ResetAbandonedTimer 達成的
  12. 在連線連接一段時間後關閉連線。基於年齡在回傳到池時關閉。
  13. 當懷疑連線已放棄時,取得 JMX 通知和日誌記錄。這類似於 removeAbandonedTimeout,但它不採取任何動作,只報告資訊。這是使用 suspectTimeout 屬性達成的。
  14. 連線可以從 java.sql.Driverjavax.sql.DataSourcejavax.sql.XADataSource 擷取。這是使用 dataSourcedataSourceJNDI 屬性達成的。
  15. XA 連線支援

如何使用

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。用於設定連線池為 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

類型應始終為 javax.sql.DataSourcejavax.sql.XADataSource

根據類型,將建立 org.apache.tomcat.jdbc.pool.DataSourceorg.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

(字串) 此池建立的連線的預設 TransactionIsolation 狀態。下列其中之一:(請參閱 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

(整數) 同時可以從此池分配的最大主動連線數。預設值為 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

(int) 空閒連線驗證/清理執行緒執行間隔的毫秒數。此值不應設定在 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;]*。注意 - 「使用者」和「密碼」屬性將明確傳遞,因此不需要包含在這裡。預設值為 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) 如果您希望以真正的 FIFO 方式公平處理對 getConnection 的呼叫,請設定為 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

(長整數)以毫秒為單位,重新建立連線前保持連線的時間。當從池中借用連線時,池會檢查是否已達到 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) 呼叫,並僅傳回在全球設定的屬性 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 攔截器

使用 jdbcInterceptors 屬性設定 JDBC 攔截器。此屬性包含分號分隔的類別名稱清單。如果類別名稱不是完全限定,它將加上 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 建立的所有陳述式,並在連線傳回池時關閉這些陳述式。

屬性 說明
trace

(布林值為字串) 啟用未關閉陳述式的追蹤。啟用後,如果連線已關閉,但陳述式未關閉,攔截器將記錄所有堆疊追蹤。預設值為 false

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

在連線上快取 PreparedStatement 和/或 CallableStatement 實例。

這些陳述會依連線快取。計數限制會針對屬於同一個池的所有連線進行全球計算。一旦計數達到max,後續陳述會不會傳回快取,並會立即關閉。

屬性 說明
prepared

(布林值為字串) 啟用快取使用 prepareStatement 呼叫建立的 PreparedStatement 執行個體。預設值為 true

callable

(布林值為字串) 啟用快取使用 prepareCall 呼叫建立的 CallableStatement 執行個體。預設值為 false

max

(int 為字串) 連線池中快取陳述會計數的限制。預設值為 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

(int 為字串) 設定查詢逾時秒數。小於或等於零的值會停用此功能。預設值為 1 秒。

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

追蹤查詢效能,並在查詢超過失敗時間門檻時發出記錄條目。使用的記錄層級為 WARN

屬性 說明
threshold

(int 為字串) 查詢必須超過的毫秒數,才會發出記錄警示。預設值為 1000 毫秒。

maxQueries

(int 為字串) 為了保留記憶體空間,追蹤的最大查詢數。小於或等於 0 的值會停用此功能。預設值為 1000

logSlow

(布林值為字串) 如果您想要記錄緩慢查詢,請設定為 true。預設值為 true

logFailed

(布林值為字串) 如果您想要記錄失敗查詢,請設定為 true。預設值為 false

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

延伸 SlowQueryReport,除了記錄條目之外,還會發出 JMX 通知,供監控工具做出反應。繼承其父類別的所有屬性。此類別使用 Tomcat 的 JMX 引擎,因此在 Tomcat 容器外無法運作。預設情況下,如果啟用,JMX 通知會透過 ConnectionPool mbean 傳送。如果 notifyPool=falseSlowQueryReportJmx 也可以註冊 MBean。

屬性 說明
notifyPool

(布林值為字串) 如果您想要 JMX 通知傳送至 SlowQueryReportJmx MBean,請設定為 false。預設值為 true

objectName

(字串) 定義一個有效的 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 屬性進行回收。使用此攔截器,它會在您對連線執行作業或成功執行查詢時,重設結帳計時器。

屬性 說明

程式碼範例

可以在 Tomcat 文件 中找到其他關於 JDBC 使用的 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 規格,方法名稱和 static final String 會出現在共享常數池中,因此參考比較應該會成功。當然也可以這樣做

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

可以在 Tomcat 文件 中找到其他關於 JDBC 使用的 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 建置,但會產生發行成品。只有函式庫本身。