成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

接上篇文章《LimitLatch 在 Tomcat 中的應用》

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

Tomcat

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

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

一、DBCP連接的生命周期

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

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

二、連接池的初始化

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

  1. protected synchronized DataSource createDataSource()  //同步方法,防止并發(fā)請求時創(chuàng)建多個連接池 
  2.             throws SQLException { 
  3.             if (closed) { 
  4.                 throw new SQLException("Data source is closed"); 
  5.             } 
  6.                 
  7.             // 如果連接池已經被初始化,直接返回PoolingDataSource 
  8.                // Return the pool if we have already created it 
  9.             if (dataSource != null) { 
  10.                 return (dataSource); 
  11.             } 
  12.                // 1.創(chuàng)建連接工廠,用于生產物理連接 
  13.                // create factory which returns raw physical connections 
  14.             ConnectionFactory driverConnectionFactory = createConnectionFactory(); 
  15.             // 2.創(chuàng)建、配置連接池,該池即為GenericObjectPool對象 
  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.又一個連接工廠,生產的是物理連接的包裝對象,供GenericObjectPool調用 
  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ù)庫驅動類名,加載該驅動,并獲取Driver實例。此處需要注意的是,首先會在TOMCAT_HOME/lib下加載驅動類,找不到才會使用WebappClassLoader加載,因此如果在tomcat的lib目錄和應用的lib目錄同時存在數(shù)據(jù)庫驅動,后者是無效的。***,使用獲取到的Driver實例和連接的相關屬性配置創(chuàng)建了一個連接工廠DriverConnectionFactory的實例并返回,DriverConnectionFactory的作用就是通過Driver實例和屬性配置生產物理連接。

2. 生成池

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

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

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

3. statement緩存池

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

4. 對象池工廠

前面說到GenericObjectPool中需要一個工廠來管理對象的部分生命周期,在這一步生成了PoolableConnectionFactory的實例作為對象池工廠。在準備就緒之后,BasicDataSource還會調用對象池工廠的5個生命周期方法,用以校驗整個流程完整無誤。

5. 封裝

該步驟將前面準備完成的GenericObjectPool池封裝為PoolingDataSource,以后的連接獲取均通過該PoolingDataSource的getConnection方法返回。連接實際為在前述GenericObjectPool的池中獲取,然后封裝為PoolGuardConnectionWrapper,該類在調用createStatement、commit等方法時均會檢查連接是否已經關閉。同樣的,statement在創(chuàng)建時也被封裝為了 DelegatingPreparedStatement、DelegatingStatement、DelegatingCallableStatement等,用以檢查是否關閉,進行資源回收等。

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

三、獲取連接

下面重點關注在連接池中獲取連接的過程,即Commons Pool中GenericObjectPool的borrowObject方法。

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

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

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

1. 如果有空閑對象且等待獲取對象的_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();//取出***個等待線程 
  5.                 latch.setPair((ObjectTimestampPair) _pool.removeFirst());//將池中空閑連接分配至線程 
  6.                 _numInternalProcessing++; 
  7.                 synchronized (latch) { 
  8.                     latch.notify();//通知等待該連接的線程 
  9.                 } 
  10.             } else { 
  11.                 break; 
  12.             } 
  13.   } 

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

  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);//標識可創(chuàng)建新的連接 
  6.                 _numInternalProcessing++; 
  7.                 synchronized (latch) { 
  8.                     latch.notify(); 
  9.                 } 
  10.             } else { 
  11.                 break; 
  12.             } 

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

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

四、歸還連接

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

當調用連接的close方法時,實際會調用PoolableConnection的close方法。

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

五、語句緩存

前面說到,statement緩存池使用了GenericKeyedObjectPoolFactory實現(xiàn)。在對象池真正創(chuàng)建連接(makeObject)的時候,由PoolableObjectFactory調用底層的DriverConnectionFactory來創(chuàng)建物理連接,然后進行包裝。如果配置了使用語句緩存,則中間會多包裝一層PoolingConnection。PoolingConnection重載了prepareStatement等方法,負責在創(chuàng)建語句時首先到statement緩存池獲取。可以看到,DBCP的語句緩存是通過層層包裝(裝飾模式)來實現(xiàn)的。

六、總結一下

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

【本文為51CTO專欄作者“侯樹成”的原創(chuàng)稿件,轉載請通過作者微信公眾號『Tomcat那些事兒』獲取授權】

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

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2010-05-14 15:32:51

配置MySQL

2010-06-04 10:31:05

tomcat MySQ

2009-06-15 13:24:46

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

2010-12-27 09:59:11

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

2017-06-14 23:42:27

大數(shù)據(jù)數(shù)據(jù)源架構

2021-03-10 19:01:02

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

2011-08-30 15:10:47

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

2010-06-12 16:54:19

2023-11-27 09:16:53

Python數(shù)據(jù)源類型

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ù)預處理

2013-06-07 10:05:18

2009-09-08 11:09:39

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

2009-09-15 17:15:33

Linq排序

2024-10-30 10:22:17

2022-02-21 08:21:00

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

2020-12-31 07:55:33

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

2009-07-28 14:22:05

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

2023-01-26 01:09:31

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

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品日产欧美久久久久 | 狠狠操狠狠干 | 97超在线视频 | 一区二区三区国产好 | 国产成人福利视频在线观看 | 在线成人免费观看 | 国产中文字幕av | 国产有码| 91精品国产综合久久久久久丝袜 | 国产乱一区二区三区视频 | 97国产一区二区精品久久呦 | 懂色中文一区二区三区在线视频 | 中国一级大毛片 | 天天综合久久 | 99久久夜色精品国产亚洲96 | 日韩欧美精品一区 | av一区二区三区四区 | 国产女人第一次做爰毛片 | 亚洲精品二三区 | 婷婷综合在线 | 亚洲欧美中文日韩在线v日本 | 精品一区二区三区在线观看 | 国产亚洲一区二区三区在线观看 | 国产一区二区久久久 | 成人在线观看免费 | 精品福利一区二区三区 | 欧美国产精品一区二区三区 | 日韩视频一区二区三区 | 超碰成人免费 | 97国产爽爽爽久久久 | 国产精品美女在线观看 | 欧美久久天堂 | 欧美成人aaa级毛片在线视频 | 久久国内精品 | 天天曰夜夜 | 综合国产 | 国产免费一区 | 精品国产乱码久久久久久88av | www.色午夜.com | 成人久久久| 亚洲视频欧美视频 |