手寫DAO框架(七)-如何保證鏈接可用

版權聲明:本文爲博客園博主「水木桶」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://www.cnblogs.com/shuimutong/p/11408219.htmlhtml

背景

手寫DAO框架系列先後更新了5篇文章,外加1篇使用示例,GDAO框架至此已經初具雛形,簡單的使用不成問題。接下來就是對框架進行不斷的優化。java

順便說一下性能

手寫DAO框架(六)-後續之框架使用示例 此篇文章對GDAO的性能較少說起,這裏就簡單的記載一下,後續有機會再更新。mysql

數據示例:id:14005  name:BatchName-4009  age:52,其中id是自動生成。git

數據庫是mariadb,和測試程序位於同一臺機器github

測試數據條數5000條,一次調用sql

userDao.add(uds[i]);數據庫

方法插入一條數據。apache

系統 配置 結果
macOS 2c4t,8g,固態硬盤 總耗時:8629.0ms,平均耗時:1.726ms
Win10 6c6t,24g,固態硬盤 總耗時:5153.0ms,平均耗時:1.031ms

 

 

 

 

 

1、當前問題分析

對於一個提供鏈接池功能的DAO框架,若是保存的鏈接失效了沒法自動移除池,若是鏈接數據庫的網絡出現閃斷鏈接沒法繼續使用,只能經過重啓服務來達到初始化鏈接的目的,這樣的作法顯然是不夠優雅的。網絡

爲了提升框架的穩定性,因此決定對框架的鏈接部分作一次優化。app

2、需求整理

經過網上查資料,擬定了幾個點。

一、自動重連

autoReconnect=true

JDBC經過配置可實現

二、鏈接有效性檢測

a、配置鏈接檢測語句。備註:有的數據庫Driver支持ping(),可使用。

三、鏈接泄露檢查

當鏈接從鏈接池借出後,長時間(配置時間)不歸還,將強制回收。

四、瞭解到的其餘問題

若是鏈接閒置時間過長,可能被mysql主動關閉。

正常的使用-歸還(鏈接放到隊列,隊列是先進先出,下次再取,造成一個循環)流程,可避免鏈接閒置時間過長,暫緩優化。

優化點肯定

二、鏈接有效性檢測

三、鏈接泄露檢查

3、編碼實現

博主開始寫代碼了,須要一段時間

鏈接有效性檢測,根據配置來檢測,代碼參考MysqlValidConnectionChecker。

鏈接泄露檢查,經過啓一條線程,根據配置時間進行檢查。

----------------------通過了好幾天的編寫,代碼完成了---------------------------------------------

直接上代碼。

一、增長配置

#########v03##########
#鏈接檢測語句
checkConnectionValidationQuery=select 1
#歸還鏈接時檢測鏈接,true false
checkConnectionWhenReturn=true
#定時檢測鏈接間隔時長(分鐘)
periodCheckConnectionTimeMin=10
#鏈接泄露檢測
connectionLeakCheck=true
#鏈接泄露檢測間隔時長(分鐘)
connectionLeakCheckPeriodTimeMin=10
#強制歸還鏈接時長(小時)
forceReturnConnectionTimeHour=6

 

說明:

1)配置能夠分爲1個基礎,3個部分。

1個基礎是指鏈接檢測語句,3個部分分別對應歸還鏈接時檢測鏈接、定時檢測鏈接間隔時長、鏈接泄露檢測

2)定時檢測主要是防止鏈接中斷了不能自動生成新的鏈接,鏈接間隔時長若是設爲0,則不會定時檢測。

二、主要代碼

  1 package me.lovegao.gdao.connection;
  2 
  3 import java.sql.Connection;
  4 import java.sql.SQLException;
  5 import java.util.ArrayList;
  6 import java.util.HashSet;
  7 import java.util.Iterator;
  8 import java.util.List;
  9 import java.util.Map;
 10 import java.util.Map.Entry;
 11 import java.util.Properties;
 12 import java.util.Queue;
 13 import java.util.Set;
 14 import java.util.concurrent.ConcurrentHashMap;
 15 import java.util.concurrent.ConcurrentLinkedQueue;
 16 import java.util.concurrent.ExecutorService;
 17 import java.util.concurrent.Executors;
 18 
 19 import org.apache.commons.lang3.StringUtils;
 20 import org.apache.commons.lang3.math.NumberUtils;
 21 import org.slf4j.Logger;
 22 import org.slf4j.LoggerFactory;
 23 
 24 import me.lovegao.gdao.bean.SystemConstant;
 25 import me.lovegao.gdao.util.ConnectionUtil;
 26 
 27 /**
 28  * 第二版簡易鏈接池實現<br/>
 29  * 主要增長鏈接有效性檢測,包括歸還鏈接時檢測,定時檢測鏈接
 30  * 
 31  * @author simple
 32  *
 33  */
 34 public class SimpleV2ConnectionPool extends SimpleConnectionPool {
 35     private final static Logger log = LoggerFactory.getLogger(SimpleV2ConnectionPool.class);
 36     private ExecutorService ES;
 37     // 歸還鏈接時檢測鏈接,這步最好作成異步的,避免影響歸還速度
 38     private boolean checkConnectionWhenReturn = false;
 39     // 鏈接檢測語句
 40     private String checkConnectionValidationQuery;
 41     // 定時檢測鏈接的時間(分鐘)
 42     private int periodCheckConnectionTimeMin;
 43     /** 待檢測鏈接 **/
 44     private volatile Queue<Connection> TO_CHECK_CONNECTION_POOL;
 45     //查詢超時時間
 46     private final int QUERY_TIMEOUT_SECONDS;
 47     //鏈接泄露檢測
 48     private boolean checkConnectionLeak = false;
 49     //鏈接泄露檢測間隔時長-分鐘
 50     private int checkConnectionLeakPeriodTimeMin = 30;
 51     //強制歸還鏈接時長(小時)
 52     private double forceReturnConnectionTimeHour;
 53     //鏈接最大空閒時長(小時)
 54 //    private double connectionMaxIdleTimeHour;
 55     /**鏈接最後借出時間**/
 56     private Map<Integer, Long> CONNECTION_OUT_TIME_MAP_POOL = null;
 57 
 58     public SimpleV2ConnectionPool(Properties properties) throws Exception {
 59         super(properties);
 60         QUERY_TIMEOUT_SECONDS = super.getQueryTimeoutSecond();
 61         initProp(properties);
 62         initCheck();
 63     }
 64     
 65     private void initProp(Properties properties) {
 66         //鏈接有效性檢測配置
 67         if (properties.containsKey(SystemConstant.STR_CHECK_CONNECTION_VALIDATION_QUERY)) {
 68             checkConnectionValidationQuery = properties
 69                     .getProperty(SystemConstant.STR_CHECK_CONNECTION_VALIDATION_QUERY);
 70             String checkWhenReturn = properties.getProperty(SystemConstant.STR_CHECK_CONNECTION_WHEN_RETURN);
 71             if (checkWhenReturn.toLowerCase().equals("true")) {
 72                 checkConnectionWhenReturn = true;
 73             }
 74             if (properties.containsKey(SystemConstant.STR_PERIOD_CHECK_CONNECTION_TIME_MIN)) {
 75                 String periodTimeStr = properties.getProperty(SystemConstant.STR_PERIOD_CHECK_CONNECTION_TIME_MIN);
 76                 if (StringUtils.isNumeric(periodTimeStr)) {
 77                     periodCheckConnectionTimeMin = Integer.parseInt(periodTimeStr);
 78                 }
 79             }
 80         }
 81         //鏈接泄露檢測配置
 82         if (properties.containsKey(SystemConstant.STR_CONNECTION_LEAK_CHECK)) {
 83             String leakCheckStr = properties.getProperty(SystemConstant.STR_CONNECTION_LEAK_CHECK);
 84             if (leakCheckStr.toLowerCase().equals("true")) {
 85                 String leakCheckPeriodTimeStr = properties.getProperty(SystemConstant.STR_CONNECTION_LEAK_CHECK_PERIOD_TIME_MIN);
 86                 if (StringUtils.isNumeric(leakCheckPeriodTimeStr)) {
 87                     checkConnectionLeakPeriodTimeMin = Integer.parseInt(leakCheckPeriodTimeStr);
 88                 }
 89                 String forceReturnTimeStr = properties.getProperty(SystemConstant.STR_FORCE_RETURN_CONNECTION_TIME_HOUR);
 90                 if (NumberUtils.isNumber(forceReturnTimeStr)) {
 91                     forceReturnConnectionTimeHour = Double.parseDouble(forceReturnTimeStr);
 92                     if(forceReturnConnectionTimeHour > 0) {
 93                         checkConnectionLeak = true;
 94                     }
 95                 }
 96                 //最大空閒檢測,功能上和定時檢測鏈接重複,暫時不開發。
 97                 
 98                 //須要同時配置(強制歸還時間)才能檢測鏈接泄露
 99                 if(forceReturnConnectionTimeHour > 0) {
100                     checkConnectionLeak = true;
101                 }
102             }
103         }
104         StringBuilder infoSb = new StringBuilder();
105         infoSb.append("SimpleV2ConnectionPoolInitDone------")
106             .append(",checkConnectionValidationQuery:").append(checkConnectionValidationQuery)
107             .append(",checkConnectionWhenReturn:").append(checkConnectionWhenReturn)
108             .append(",periodCheckConnectionTimeMin:").append(periodCheckConnectionTimeMin)
109             .append(",checkConnectionLeak:").append(checkConnectionLeak)
110             .append(",checkConnectionLeakPeriodTimeMin:").append(checkConnectionLeakPeriodTimeMin)
111             .append(",forceReturnConnectionTimeHour:").append(forceReturnConnectionTimeHour);
112         System.out.println(infoSb.toString());
113     }
114 
115     @Override
116     public Connection getConnection() throws Exception {
117         Connection conn = super.getConnection();
118         if(checkConnectionLeak) {
119             int connHashCode = conn.hashCode();
120             CONNECTION_OUT_TIME_MAP_POOL.put(connHashCode, System.currentTimeMillis());
121         }
122         return conn;
123     }
124 
125     @Override
126     public void returnConnection(Connection conn) {
127         //檢測鏈接泄露
128         if(checkConnectionLeak) {
129             int connHashCode = conn.hashCode();
130             //鏈接超時過長,已經被主動移除
131             if(!CONNECTION_OUT_TIME_MAP_POOL.containsKey(connHashCode)) {
132                 return;
133             } else {
134                 CONNECTION_OUT_TIME_MAP_POOL.remove(connHashCode);
135             }
136         }
137         //檢測歸還鏈接
138         if (checkConnectionWhenReturn) {
139             if(TO_CHECK_CONNECTION_POOL.isEmpty()) {
140                 synchronized(TO_CHECK_CONNECTION_POOL) {
141                     if(TO_CHECK_CONNECTION_POOL.isEmpty()) {
142                         TO_CHECK_CONNECTION_POOL.add(conn);
143                         TO_CHECK_CONNECTION_POOL.notifyAll();
144                     } else {
145                         TO_CHECK_CONNECTION_POOL.add(conn);
146                     }
147                 }
148             } else {
149                 TO_CHECK_CONNECTION_POOL.add(conn);
150             }
151         } else {
152             superReturnConnection(conn);
153         }
154     }
155     
156 
157     @Override
158     public void closeConnectionPool() {
159         if(ES != null) {
160             ES.shutdownNow();
161         }
162         super.closeConnectionPool();
163     }
164 
165     private void superReturnConnection(Connection conn) {
166         super.returnConnection(conn);
167     }
168     
169     private Connection superGetByConnectionHashCode(int hashCode) {
170         return super.getByConnectionHashCode(hashCode);
171     }
172 
173     // 初始化檢查
174     private void initCheck() {
175         int threadPoolSize = 0;
176         if(checkConnectionWhenReturn) {
177             threadPoolSize += 2;
178             TO_CHECK_CONNECTION_POOL = new ConcurrentLinkedQueue();
179         }
180         if(periodCheckConnectionTimeMin > 0) {
181             threadPoolSize++;
182         }
183         //須要同時配置(強制歸還時間、最大空閒時間)才能檢測鏈接泄露
184         if(checkConnectionLeak) {
185             threadPoolSize++;
186             CONNECTION_OUT_TIME_MAP_POOL = new ConcurrentHashMap();
187         }
188         if(threadPoolSize > 0) {
189             ES = Executors.newFixedThreadPool(threadPoolSize);
190             // 檢查歸還鏈接
191             if(checkConnectionWhenReturn) {
192                 //啓兩個線程同時檢測
193                 for(int i=0; i<2; i++) {
194                     ES.execute(new ReturnConnectionCheck());
195                 }
196             }
197             //定時檢測鏈接
198             if (periodCheckConnectionTimeMin > 0) {
199                 ES.execute(new ConnectionPeriodCheck());
200             }
201             //鏈接泄露檢測
202             if(checkConnectionLeak) {
203                 ES.execute(new ConnectionLeakCheck());
204             }
205         }
206     }
207 
208     /**
209      * 鏈接泄露定時檢測
210      * @author simple
211      *
212      */
213     class ConnectionLeakCheck implements Runnable {
214         int sleepTimeMs = checkConnectionLeakPeriodTimeMin * 60 * 1000;
215         @Override
216         public void run() {
217             Set<Integer> preConnHashCodeSet = new HashSet();
218             while (true) {
219                 if(ES.isShutdown()) {
220                     break;
221                 }
222                 try {
223                     Thread.sleep(sleepTimeMs);
224                 } catch (Exception e) {
225                     log.error("ConnectionLeakCheckSleepException", e);
226                 }
227                 try {
228                     checkConnectionLeak(preConnHashCodeSet);
229                 } catch (Exception e) {
230                     log.error("ConnectionLeakCheckException", e);
231                 }
232             }
233         }
234     }
235     
236     //檢測鏈接泄露
237     private void checkConnectionLeak(Set<Integer> preConnHashCodeSet) throws Exception {
238         if(CONNECTION_OUT_TIME_MAP_POOL.size() < 1) {
239             preConnHashCodeSet = new HashSet();
240         } else {
241             Iterator<Entry<Integer, Long>> connHashCodeIt = CONNECTION_OUT_TIME_MAP_POOL.entrySet().iterator();
242             //先對比先後兩次的鏈接,若是有相同的,再檢測相同的鏈接
243             if(preConnHashCodeSet.size() == 0) {
244                 while(connHashCodeIt.hasNext()) {
245                     preConnHashCodeSet.add(connHashCodeIt.next().getKey());
246                 }
247             } else {
248                 StringBuilder logSb = new StringBuilder();
249                 long timeFlag = (long) (System.currentTimeMillis() - forceReturnConnectionTimeHour * 3600 * 1000);
250                 logSb.append("ConnectionLeakCheck---")
251                     .append(",timeFlag:").append(timeFlag)
252                     .append(",forceReturnConnectionTimeHour:").append(forceReturnConnectionTimeHour);
253                 List<Integer> toCloseConnectionHashCodeList = new ArrayList();
254                 logSb.append(",toCloseConn,{");
255                 //過濾出兩次集合重合,且已經超時的元素
256                 while(connHashCodeIt.hasNext()) {
257                     Entry<Integer, Long> connEntry = connHashCodeIt.next();
258                     int connHashCode = connEntry.getKey();
259                     if(preConnHashCodeSet.contains(connHashCode) && connEntry.getValue() < timeFlag) {
260                         toCloseConnectionHashCodeList.add(connHashCode);
261                         logSb.append(connHashCode).append(":").append(connEntry.getValue()).append(",");
262                     }
263                 }
264                 logSb.append("}");
265                 if(toCloseConnectionHashCodeList.size() > 0) {
266                     for(Integer connHashCode : toCloseConnectionHashCodeList) {
267                         Connection conn = superGetByConnectionHashCode(connHashCode);
268                         if(conn != null) {
269                             try {
270                                 conn.close();
271                             } catch (SQLException e) {
272                                 log.error("closeConnectionException", e);
273                             }
274                             CONNECTION_OUT_TIME_MAP_POOL.remove(connHashCode);
275                             superReturnConnection(conn);
276                         }
277                     }
278                 }
279                 log.info(logSb.toString());
280                 //進行過一次檢測以後,對以前存儲的進行初始化
281                 preConnHashCodeSet = new HashSet();
282             }
283         }
284     }
285     
286     /**
287      * 歸還鏈接檢測
288      * @author simple
289      *
290      */
291     class ReturnConnectionCheck implements Runnable {
292         @Override
293         public void run() {
294             while (true) {
295                 if(ES.isShutdown()) {
296                     break;
297                 }
298                 Connection toCheckConn = TO_CHECK_CONNECTION_POOL.poll();
299                 if (toCheckConn == null) {
300                     try {
301                         synchronized(TO_CHECK_CONNECTION_POOL) {
302                             TO_CHECK_CONNECTION_POOL.wait();
303                         }
304                     } catch (InterruptedException e) {
305                         log.error("checkReturnConnectionWaitException", e);
306                     }
307                 } else {
308                     boolean canUse = ConnectionUtil.isValidConnection(toCheckConn, checkConnectionValidationQuery,
309                             QUERY_TIMEOUT_SECONDS);
310                     if (!canUse) {
311                         try {
312                             toCheckConn.close();
313                         } catch (SQLException e) {
314                             log.error("checkReturnConnectionCloseConnException", e);
315                         }
316                     }
317                     superReturnConnection(toCheckConn);
318                 }
319             }
320         }
321     }
322     
323     /**
324      * 鏈接定時檢測
325      * @author simple
326      *
327      */
328     class ConnectionPeriodCheck implements Runnable {
329         int sleepTimeMs = periodCheckConnectionTimeMin * 60 * 1000;
330         @Override
331         public void run() {
332             while (true) {
333                 if(ES.isShutdown()) {
334                     break;
335                 }
336                 try {
337                     Thread.sleep(sleepTimeMs);
338                 } catch (Exception e) {
339                     log.error("checkReturnConnectionSleepException", e);
340                 }
341                 while(true) {
342                     //是否繼續檢測
343                     boolean continueCheck = false;
344                     Connection toCheckConn = null;
345                     try {
346                         toCheckConn = getConnection();
347                         if (toCheckConn != null) {
348                             boolean canUse = ConnectionUtil.isValidConnection(toCheckConn,
349                                     checkConnectionValidationQuery, QUERY_TIMEOUT_SECONDS);
350                             if (!canUse) {
351                                 toCheckConn.close();
352                                 //鏈接不可用,繼續檢測其餘鏈接是否正常
353                                 continueCheck = true;
354                                 log.info("oneConnectionCannotUse,closeIt.....");
355                             }
356                         }
357                     } catch (Exception e) {
358                         log.error("checkReturnConnectionException", e);
359                         continueCheck = true;
360                     } finally {
361                         if (toCheckConn != null) {
362                             superReturnConnection(toCheckConn);
363                         }
364                     }
365                     if(continueCheck) {
366                         log.info("checkOneConnectionCannotUse,beginToCheckOtherConnection....");
367                         try {
368                             Thread.sleep(500);
369                         } catch (InterruptedException e) {
370                             log.error("checkReturnConnectionWaitException", e);
371                         }
372                     } else {
373                         break;
374                     }
375                 }
376             }
377         }
378     }
379 }

 

三、主要思路

1)歸還鏈接時檢測鏈接的思路

歸還鏈接的時候,若是不採用異步,那麼歸還鏈接的線程必須等待鏈接確認完畢以後才能繼續執行,這樣作感受性能不是最優的。

因此引入了異步,歸還鏈接時,鏈接直接放到一個待檢測的容器裏,不須要等待檢測完以後再返回。

待檢測鏈接由檢測線程異步進行檢測。檢測現場從待檢測容器裏取鏈接進行檢測,必然會出現空的狀況。

出現了空的狀況怎麼作好呢,是在那裏自旋等待?是休眠一段時間再檢測?仍是等待呢?

自旋等待,浪費計算資源;休眠的話,休眠時長很差肯定,誰知道下一毫秒會發生什麼?萬一由於鏈接未及時檢測出現了鏈接用盡,豈不是很尷尬?

因此,我選擇了等待,當線程歸還時,主動喚醒等待線程。

代碼實現以後,測試的時候,我發現運行報錯。wait()、notifyAll()方法不是那樣用的,須要獲取鎖。

添加了鎖以後,爲了儘可能減小同步帶來的性能損失,我採起了寫單例時常常提到的雙重檢查:我不須要每次都要拿鎖、通知,只須要在待檢測鏈接池是空的時候才須要進行拿鎖、通知。、

2)鏈接泄露檢測的思路

鏈接泄露檢測,主要是爲了防止鏈接被借出去以後,好久都沒有歸還的情景。

好久不歸還,這個鏈接還佔着鏈接池的坑,卻無法被複用,因此須要進行檢測。

檢測須要肯定的就是,取出多久算久?而後就是,多久檢測一次?

具體實現的時候,還有一個問題,就是強制歸還的鏈接應該怎麼歸還?直接放回鏈接池嗎?萬一鏈接真的還在被別的線程使用怎麼辦?

因此這裏,我採起先把鏈接關閉了,而後再歸還。

3)定時檢測鏈接的思路

爲了保持鏈接池的鏈接都是最終可用的,因此須要對鏈接池的鏈接進行定時的檢測。

若是鏈接不可用,就把鏈接關閉,而後從鏈接池去除。

四、測試代碼

1)配置

##驅動名稱
driverName=com.mysql.jdbc.Driver
##鏈接url
connectionUrl=jdbc:mysql://localhost:3306/simple?autoReconnect=true&useServerPrepStmts=false&rewriteBatchedStatements=true&connectTimeout=1000&useUnicode=true&characterEncoding=utf-8
##用戶名
userName=simple
##用戶密碼
userPassword=123456
##初始化鏈接數
initConnectionNum=10
##最大鏈接數
maxConnectionNum=50
##最大查詢等待時間
maxQueryTime=3
#########v03##########
#歸還鏈接時檢測鏈接,true false
checkConnectionWhenReturn=true
#鏈接檢測語句
checkConnectionValidationQuery=select 1
#定時檢測鏈接間隔時長(分鐘)
periodCheckConnectionTimeMin=1
#鏈接泄露檢測
connectionLeakCheck=false
#鏈接泄露檢測間隔時長(分鐘)
connectionLeakCheckPeriodTimeMin=1
#強制歸還鏈接時長(小時)
forceReturnConnectionTimeHour=0.01

 

2)測試代碼

package me.lovegao.gdao.connpool;

import java.sql.Connection;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import me.lovegao.gdao.bean.SystemConstant;
import me.lovegao.gdao.connection.IConnectionPool;
import me.lovegao.gdao.connection.SimpleV2ConnectionPool;

public class V2ConnectionPoolTest {
    private final static Logger log = LoggerFactory.getLogger(V2ConnectionPoolTest.class);

    public static void main(String[] args) throws Exception {
        String dbPath = "mysql2.properties";
        log.info("hello-----------------");
        log.warn("hello-----------------");
        periodCheckConnection(dbPath);
    }
    
    /**
     * 鏈接泄露檢測
     * @param dbPath
     * @throws Exception
     */
    public static void checkConnectionLeak(String dbPath) throws Exception {
        Properties dbProp = CommonUtil.loadProp(dbPath);
        dbProp.setProperty(SystemConstant.STR_PERIOD_CHECK_CONNECTION_TIME_MIN, "10");
        dbProp.setProperty(SystemConstant.STR_CONNECTION_LEAK_CHECK, "true");
        IConnectionPool connPool = new SimpleV2ConnectionPool(dbProp);
        Connection conn = connPool.getConnection();
        Thread.sleep(240000);
        connPool.returnConnection(conn);
        Thread.sleep(61000);
        connPool.closeConnectionPool();
    }
    
    /**
     * 鏈接定時檢測
     * @param dbPath
     * @throws Exception
     */
    public static void periodCheckConnection(String dbPath) throws Exception {
        Properties dbProp = CommonUtil.loadProp(dbPath);
        dbProp.setProperty(SystemConstant.STR_PERIOD_CHECK_CONNECTION_TIME_MIN, "1");
        IConnectionPool connPool = new SimpleV2ConnectionPool(dbProp);
        Thread.sleep(120000);
        Connection conn = connPool.getConnection();
        connPool.returnConnection(conn);
        Thread.sleep(2061000);
//        connPool.closeConnectionPool();
    }
    
    /**
     * 歸還鏈接檢測
     * @param dbPath
     * @throws Exception
     */
    public static void checkWhenReturn(String dbPath) throws Exception {
        Properties dbProp = CommonUtil.loadProp(dbPath);
        IConnectionPool connPool = new SimpleV2ConnectionPool(dbProp);
        Connection conn = connPool.getConnection();
        conn.close();
        connPool.returnConnection(conn);
        Thread.sleep(20000);
        connPool.closeConnectionPool();
    }

}

 

 

我是分佈執行的測試,經過debug來校驗的流程,面對這種項目,不知道單測該如何寫。

我是經過中間把數據庫關了,又打開,來鏈接定時檢測的。結果證實沒有問題。

框架優化版本已提交到git:https://github.com/shuimutong/gdao.git,歡迎指點

相關測試代碼:https://github.com/shuimutong/useDemo.git  ./gdao-demo下。 

 

-----------------------本文完------------------------------

相關文章
相關標籤/搜索