自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Tomcat 的數(shù)據(jù)源(一)

開(kāi)發(fā) 開(kāi)發(fā)工具
在Tomcat8之前,tomcat使用的默認(rèn)數(shù)據(jù)源實(shí)現(xiàn)為DBCP,tomcat8之后的默認(rèn)數(shù)據(jù)源實(shí)現(xiàn)為DBCP2。本文基于Tomcat7.0.78(DBCP1.4),分析tomcat7數(shù)據(jù)源的源碼實(shí)現(xiàn),Tomcat JDBC Connection Pool以及DBCP2的實(shí)現(xiàn)在后續(xù)的文章中進(jìn)行分析。

接上篇文章《LimitLatch 在 Tomcat 中的應(yīng)用》

在Tomcat8之前,tomcat使用的默認(rèn)數(shù)據(jù)源實(shí)現(xiàn)為DBCP,tomcat8之后的默認(rèn)數(shù)據(jù)源實(shí)現(xiàn)為DBCP2。本文基于Tomcat7.0.78(DBCP1.4),分析tomcat7數(shù)據(jù)源的源碼實(shí)現(xiàn),Tomcat JDBC Connection Pool以及DBCP2的實(shí)現(xiàn)在后續(xù)的文章中進(jìn)行分析。

Tomcat

首先看一下,tomcat文檔在宣傳Tomcat JDBC Connection Pool時(shí)指出的DBCP(1.x)的不足:

  1. 單線程,為了保證線程安全,在獲取和歸還對(duì)象時(shí)需要給整個(gè)連接池上鎖。
  2. 慢,隨著CPU數(shù)量的增長(zhǎng)以及獲取、歸還對(duì)象的并發(fā)線程數(shù)的增長(zhǎng),性能堪憂(yōu),對(duì)于高并發(fā)系統(tǒng)影響很大。
  3. 超過(guò)60個(gè)類(lèi),不易維護(hù)。
  4. 不支持異步獲取鏈接,等等。

一、DBCP連接的生命周期

要想讀懂DBCP,首先得弄明白一個(gè)連接的生命周期的各個(gè)階段,存在于連接工廠、對(duì)象池和連接的使用過(guò)程中,簡(jiǎn)單描述如下:

  1. 出生,對(duì)象池調(diào)用連接工廠的makeObject方法生產(chǎn)一個(gè)連接。
  2. 校驗(yàn),通過(guò)執(zhí)行校驗(yàn)SQL,判斷當(dāng)前連接是否可用。
  3. 激活,即連接的初始化,設(shè)置連接的默認(rèn)值,如autoCommit等,在獲取連接時(shí)調(diào)用。
  4. 借用,調(diào)用對(duì)象池的borrowObject,從池中獲取(或新建)一個(gè)對(duì)象實(shí)例。
  5. 使用,應(yīng)用獲得連接后創(chuàng)建Statement,提交事務(wù)等。
  6. 歸還,當(dāng)調(diào)用連接的close方法關(guān)閉連接時(shí),實(shí)際調(diào)用對(duì)象池的returnObject方法歸還該連接。
  7. 鈍化,歸還連接時(shí)調(diào)用,回滾未提交的事務(wù),清除連接的警告,關(guān)閉未關(guān)閉的資源如Statement等。
  8. 銷(xiāo)毀,當(dāng)歸還連接時(shí)連接已關(guān)閉、校驗(yàn)不通過(guò)或者發(fā)生異常等,則應(yīng)當(dāng)銷(xiāo)毀該連接而不是歸還到連接池中,清理該連接對(duì)應(yīng)的資源,并且關(guān)閉物理連接。

二、連接池的初始化

當(dāng)我們通過(guò)JNDI拿到數(shù)據(jù)源并調(diào)用其getConnection方法時(shí),實(shí)際獲取到的數(shù)據(jù)源實(shí)現(xiàn)類(lèi)是BasicDataSource。BasicDataSource的主要工作就是完成數(shù)據(jù)源的初始化功能,該工作在***次調(diào)用數(shù)據(jù)源的getConnection方法時(shí)完成,一旦完成該部分工作,獲取連接的功能實(shí)際則交由PoolingDataSource類(lèi)完成,貼個(gè)代碼先:

  1. protected synchronized DataSource createDataSource()  //同步方法,防止并發(fā)請(qǐng)求時(shí)創(chuàng)建多個(gè)連接池 
  2.             throws SQLException { 
  3.             if (closed) { 
  4.                 throw new SQLException("Data source is closed"); 
  5.             } 
  6.                 
  7.             // 如果連接池已經(jīng)被初始化,直接返回PoolingDataSource 
  8.                // Return the pool if we have already created it 
  9.             if (dataSource != null) { 
  10.                 return (dataSource); 
  11.             } 
  12.                // 1.創(chuàng)建連接工廠,用于生產(chǎn)物理連接 
  13.                // create factory which returns raw physical connections 
  14.             ConnectionFactory driverConnectionFactory = createConnectionFactory(); 
  15.             // 2.創(chuàng)建、配置連接池,該池即為GenericObjectPool對(duì)象 
  16.                // create a pool for our connections 
  17.             createConnectionPool(); 
  18.             // 3.statement緩存池 
  19.                // Set up statement pool, if desired 
  20.             GenericKeyedObjectPoolFactory statementPoolFactory = null
  21.             if (isPoolPreparedStatements()) { 
  22.                 statementPoolFactory = new GenericKeyedObjectPoolFactory(null, 
  23.                             -1, // unlimited maxActive (per key) 
  24.                             GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 
  25.                             0, // maxWait 
  26.                             1, // maxIdle (per key) 
  27.                             maxOpenPreparedStatements); 
  28.             } 
  29.                //4.又一個(gè)連接工廠,生產(chǎn)的是物理連接的包裝對(duì)象,供GenericObjectPool調(diào)用 
  30.                // Set up the poolable connection factory 
  31.             createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig); 
  32.             // 5.封裝 
  33.                // Create and return the pooling data source to manage the connections 
  34.             createDataSourceInstance();      
  35.             // 6.連接初始化 
  36.             try { 
  37.                 for (int i = 0 ; i < initialSize ; i++) { 
  38.                     connectionPool.addObject(); 
  39.                 } 
  40.             } catch (Exception e) { 
  41.                 throw new SQLNestedException("Error preloading the connection pool", e); 
  42.             }         
  43.             return dataSource; 

1. 創(chuàng)建物理連接工廠

根據(jù)配置的數(shù)據(jù)庫(kù)驅(qū)動(dòng)類(lèi)名,加載該驅(qū)動(dòng),并獲取Driver實(shí)例。此處需要注意的是,首先會(huì)在TOMCAT_HOME/lib下加載驅(qū)動(dòng)類(lèi),找不到才會(huì)使用WebappClassLoader加載,因此如果在tomcat的lib目錄和應(yīng)用的lib目錄同時(shí)存在數(shù)據(jù)庫(kù)驅(qū)動(dòng),后者是無(wú)效的。***,使用獲取到的Driver實(shí)例和連接的相關(guān)屬性配置創(chuàng)建了一個(gè)連接工廠DriverConnectionFactory的實(shí)例并返回,DriverConnectionFactory的作用就是通過(guò)Driver實(shí)例和屬性配置生產(chǎn)物理連接。

2. 生成池

DBCP1.4使用了1.5.4版本的commons-pool來(lái)提供對(duì)象池功能。根據(jù)配置,有GenericObjectPool和AbandonedObjectPool兩種實(shí)現(xiàn),AbandonedObjectPool繼承了GenericObjectPool,在其基礎(chǔ)上添加了跟蹤連接泄漏的功能,以下代碼為AbandonedObjectPool獲取連接時(shí)做的工作,可以看到,一個(gè)追蹤隊(duì)列加一個(gè)獲取連接時(shí)的事件觸發(fā)即可實(shí)現(xiàn)連接泄漏追蹤的功能。

  1. public Object borrowObject() throws Exception { 
  2.         if (config != null 
  3.                 && config.getRemoveAbandoned() 
  4.                 && (getNumIdle() < 2
  5.                 && (getNumActive() > getMaxActive() - 3) ) { 
  6.             removeAbandoned();//當(dāng)可用連接數(shù)過(guò)少或即將達(dá)到***連接數(shù)時(shí),遍歷追蹤隊(duì)列,看是否存在超時(shí)歸還的連接 
  7.         } 
  8.         Object obj = super.borrowObject();//從父類(lèi)即GenericObjectPool獲取連接 
  9.         if (obj instanceof AbandonedTrace) { 
  10.             ((AbandonedTrace) obj).setStackTrace();//記錄堆棧,方便排查問(wèn)題 
  11.         } 
  12.         if (obj != null && config != null && config.getRemoveAbandoned()) { 
  13.             synchronized (trace) { 
  14.                 trace.add(obj);//獲取連接成功,添加到追蹤隊(duì)列 
  15.             } 
  16.         } 
  17.         return obj; 
  18.     } 

GenericObjectPool中有兩個(gè)重要的屬性:_factory和_pool。屬性_factory為接口PoolableObjectFactory的實(shí)例,管理了對(duì)象生命周期中的五個(gè)階段:生產(chǎn)、銷(xiāo)毀、激活、鈍化、校驗(yàn),DBCP中PoolableObjectFactory的實(shí)現(xiàn)類(lèi)為PoolableConnectionFactory,在該類(lèi)中保存了連接池的所有配置以及步驟1中的物理連接工廠等;屬性_pool中則存放了實(shí)際的所有空閑連接,其實(shí)現(xiàn)類(lèi)CursorableLinkedList為Commons Collections中的實(shí)現(xiàn),是一個(gè)雙向鏈表,GenericObjectPool在_pool的頭部獲取對(duì)象,歸還連接時(shí)根據(jù)是否LIFO策略向_pool中的頭或者尾添加對(duì)象。

3. statement緩存池

statement緩存池使用GenericKeyedObjectPoolFactory實(shí)現(xiàn),其與GenericObjectPool的各個(gè)方法的主要思路相同,而區(qū)別就是在獲取、歸還對(duì)象等操作時(shí),對(duì)應(yīng)一個(gè)key,即一個(gè)key一個(gè)池,一個(gè)Connection對(duì)象對(duì)應(yīng)多個(gè)statement緩存。

4. 對(duì)象池工廠

前面說(shuō)到GenericObjectPool中需要一個(gè)工廠來(lái)管理對(duì)象的部分生命周期,在這一步生成了PoolableConnectionFactory的實(shí)例作為對(duì)象池工廠。在準(zhǔn)備就緒之后,BasicDataSource還會(huì)調(diào)用對(duì)象池工廠的5個(gè)生命周期方法,用以校驗(yàn)整個(gè)流程完整無(wú)誤。

5. 封裝

該步驟將前面準(zhǔn)備完成的GenericObjectPool池封裝為PoolingDataSource,以后的連接獲取均通過(guò)該P(yáng)oolingDataSource的getConnection方法返回。連接實(shí)際為在前述GenericObjectPool的池中獲取,然后封裝為PoolGuardConnectionWrapper,該類(lèi)在調(diào)用createStatement、commit等方法時(shí)均會(huì)檢查連接是否已經(jīng)關(guān)閉。同樣的,statement在創(chuàng)建時(shí)也被封裝為了 DelegatingPreparedStatement、DelegatingStatement、DelegatingCallableStatement等,用以檢查是否關(guān)閉,進(jìn)行資源回收等。

6. ***進(jìn)行連接數(shù)的初始化,根據(jù)配置的最小連接數(shù),生成相應(yīng)的連接。

三、獲取連接

下面重點(diǎn)關(guān)注在連接池中獲取連接的過(guò)程,即Commons Pool中GenericObjectPool的borrowObject方法。

  1. Latch latch = new Latch(); 
  2.             ...... 
  3.             synchronized (this) { 
  4.                 ...... 
  5.                 _allocationQueue.add(latch); 
  6.              ...... 
  7.                 allocate(); 
  8.  } 

我們看到在獲取池中對(duì)象時(shí),并沒(méi)有直接去對(duì)應(yīng)的_pool(存放了空閑對(duì)象)中取,而是創(chuàng)建了一個(gè)Latch對(duì)象,然后將該對(duì)象放入一個(gè)LinkedList中,然后調(diào)用allocate方法。LinkedList中的每一個(gè)Latch都代表了一個(gè)待獲取連接的線程。

allocate是一個(gè)同步方法,做了兩部分工作:

1. 如果有空閑對(duì)象且等待獲取對(duì)象的_allocationQueue不為空,中和兩者。

  1. // First use any objects in the pool to clear the queue 
  2.         for (;;) { 
  3.             if (!_pool.isEmpty() && !_allocationQueue.isEmpty()) { 
  4.                 Latch latch = (Latch) _allocationQueue.removeFirst();//取出***個(gè)等待線程 
  5.                 latch.setPair((ObjectTimestampPair) _pool.removeFirst());//將池中空閑連接分配至線程 
  6.                 _numInternalProcessing++; 
  7.                 synchronized (latch) { 
  8.                     latch.notify();//通知等待該連接的線程 
  9.                 } 
  10.             } else { 
  11.                 break; 
  12.             } 
  13.   } 

2. 如果仍有等待獲取對(duì)象的_allocationQueue不為空且池中對(duì)象數(shù)量沒(méi)有達(dá)到***值,則可創(chuàng)建新的對(duì)象。

  1. // Second utilise any spare capacity to create new objects 
  2.         for(;;) { 
  3.             if((!_allocationQueue.isEmpty()) && (_maxActive < 0 || (_numActive + _numInternalProcessing) < _maxActive)) { 
  4.                 Latch latch = (Latch) _allocationQueue.removeFirst(); 
  5.                 latch.setMayCreate(true);//標(biāo)識(shí)可創(chuàng)建新的連接 
  6.                 _numInternalProcessing++; 
  7.                 synchronized (latch) { 
  8.                     latch.notify(); 
  9.                 } 
  10.             } else { 
  11.                 break; 
  12.             } 

執(zhí)行到這里,Latch實(shí)例存在三種情況:

  • pair屬性中拿到了需要的對(duì)象;
  • 沒(méi)有拿到對(duì)象,但mayCreate屬性為true,返回后直接創(chuàng)建新的對(duì)象;
  • 沒(méi)有拿到對(duì)象,且mayCreate屬性為false。如果是情景3,則根據(jù)配置的策略,進(jìn)行異常拋出或者阻塞的處理。阻塞會(huì)調(diào)用latch的wait方法,等待下次的allocate觸發(fā)時(shí)的notify通知,或者超時(shí)失敗拋出異常。

四、歸還連接

限于篇幅原因,后面的功能我們簡(jiǎn)單看下主要流程,感興趣的童鞋一定要翻看下源碼哦。

當(dāng)調(diào)用連接的close方法時(shí),實(shí)際會(huì)調(diào)用PoolableConnection的close方法。

  1. 查看該連接是否已經(jīng)關(guān)閉,如果是,則直接返回。
  2. 查看該連接內(nèi)部的實(shí)際物理連接是否已經(jīng)關(guān)閉,如果是,則需要銷(xiāo)毀該連接,清理資源(statements),更新監(jiān)控量。
  3. 如果一切正常,則通過(guò)連接工廠的passivateObject方法鈍化重置后,返回到對(duì)象池中。

五、語(yǔ)句緩存

前面說(shuō)到,statement緩存池使用了GenericKeyedObjectPoolFactory實(shí)現(xiàn)。在對(duì)象池真正創(chuàng)建連接(makeObject)的時(shí)候,由PoolableObjectFactory調(diào)用底層的DriverConnectionFactory來(lái)創(chuàng)建物理連接,然后進(jìn)行包裝。如果配置了使用語(yǔ)句緩存,則中間會(huì)多包裝一層PoolingConnection。PoolingConnection重載了prepareStatement等方法,負(fù)責(zé)在創(chuàng)建語(yǔ)句時(shí)首先到statement緩存池獲取??梢钥吹?,DBCP的語(yǔ)句緩存是通過(guò)層層包裝(裝飾模式)來(lái)實(shí)現(xiàn)的。

六、總結(jié)一下

DBCP1.X是一個(gè)古老的數(shù)據(jù)源實(shí)現(xiàn),1.2版本甚至可以追溯到10年之前,但時(shí)至今日,筆者仍能在眾多項(xiàng)目(主要是Spring托管)中看到他的身影,雖然一方面的原因是項(xiàng)目缺乏開(kāi)拓性,這也從側(cè)面證實(shí)了DBCP確實(shí)能夠滿(mǎn)足大多數(shù)項(xiàng)目的需求。在后面的數(shù)據(jù)源系列文章中我們將繼續(xù)分析Tomcat中其的他數(shù)據(jù)源實(shí)現(xiàn),并進(jìn)行性能測(cè)試。

【本文為51CTO專(zhuān)欄作者“侯樹(shù)成”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)作者微信公眾號(hào)『Tomcat那些事兒』獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2010-05-14 15:32:51

配置MySQL

2010-06-04 10:31:05

tomcat MySQ

2010-12-27 09:59:11

ODBC數(shù)據(jù)源

2009-06-15 13:24:46

JBoss數(shù)據(jù)源

2021-03-10 19:01:02

SQL數(shù)據(jù)源

2017-06-14 23:42:27

大數(shù)據(jù)數(shù)據(jù)源架構(gòu)

2011-08-30 15:10:47

Tomcat 6.0Oracle 10g數(shù)據(jù)源連接測(cè)試

2010-06-12 16:54:19

2023-11-27 09:16:53

Python數(shù)據(jù)源類(lèi)型

2009-07-21 17:41:58

JDBC數(shù)據(jù)源

2013-06-09 10:15:09

2021-10-18 06:54:47

數(shù)據(jù)源數(shù)據(jù)預(yù)處理

2013-06-07 10:05:18

2024-10-30 10:22:17

2009-09-08 11:09:39

LINQ數(shù)據(jù)源

2009-09-15 17:15:33

Linq排序

2022-02-21 08:21:00

微服務(wù)數(shù)據(jù)通信數(shù)據(jù)同步

2009-07-28 14:22:05

數(shù)據(jù)源控件ASP.NET

2020-12-31 07:55:33

spring bootMybatis數(shù)據(jù)庫(kù)

2023-01-26 01:09:31

配置數(shù)據(jù)源參數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)