日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Tomcat的數(shù)據(jù)源(一)

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

成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)中從網(wǎng)站色彩、結(jié)構(gòu)布局、欄目設(shè)置、關(guān)鍵詞群組等細(xì)微處著手,突出企業(yè)的產(chǎn)品/服務(wù)/品牌,幫助企業(yè)鎖定精準(zhǔn)用戶(hù),提高在線咨詢(xún)和轉(zhuǎn)化,使成都網(wǎng)站營(yíng)銷(xiāo)成為有效果、有回報(bào)的無(wú)錫營(yíng)銷(xiāo)推廣。成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)成都網(wǎng)站建設(shè)十年了,客戶(hù)滿(mǎn)意度97.8%,歡迎成都創(chuàng)新互聯(lián)客戶(hù)聯(lián)系。

在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 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;
  44. }

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.             }
  13. }

執(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è)試。

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


分享文章:Tomcat的數(shù)據(jù)源(一)
轉(zhuǎn)載源于:http://m.5511xx.com/article/dppdede.html