基础参数说明
由于DruidDataSource大部分属性都是参考DBCP的
所以可以参考下DBCP配置说明
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc_url}" /> <property name="username" value="${jdbc_user}" /> <property name="password" value="${jdbc_password}" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="5" /> <property name="minIdle" value="5" /> <property name="maxActive" value="10" /> <!-- 配置从连接池获取连接等待超时的时间 --> <property name="maxWait" value="10000" /> <!-- 配置间隔多久启动一次DestroyThread,对连接池内的连接才进行一次检测,单位是毫秒。 检测时:1.如果连接空闲并且超过minIdle以外的连接,如果空闲时间超过minEvictableIdleTimeMillis设置的值则直接物理关闭。2.在minIdle以内的不处理。 --> <property name="timeBetweenEvictionRunsMillis" value="600000" /> <!-- 配置一个连接在池中最大空闲时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <!-- 设置从连接池获取连接时是否检查连接有效性,true时,每次都检查;false时,不检查 --> <property name="testOnBorrow" value="false" /> <!-- 设置往连接池归还连接时是否检查连接有效性,true时,每次都检查;false时,不检查 --> <property name="testOnReturn" value="false" /> <!-- 设置从连接池获取连接时是否检查连接有效性,true时,如果连接空闲时间超过minEvictableIdleTimeMillis进行检查,否则不检查;false时,不检查 --> <property name="testWhileIdle" value="true" /> <!-- 检验连接是否有效的查询语句。如果数据库Driver支持ping()方法,则优先使用ping()方法进行检查,否则使用validationQuery查询进行检查。(Oracle jdbc Driver目前不支持ping方法) --> <property name="validationQuery" value="select 1 from dual" /> <!-- 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法 --> <!-- <property name="validationQueryTimeout" value="1" /> --> <!-- 打开后,增强timeBetweenEvictionRunsMillis的周期性连接检查,minIdle内的空闲连接,每次检查强制验证连接有效性. 参考:https://github.com/alibaba/druid/wiki/KeepAlive_cn --> <property name="keepAlive" value="true" /> <!-- 连接泄露检查,打开removeAbandoned功能 , 连接从连接池借出后,长时间不归还,将触发强制回连接。回收周期随timeBetweenEvictionRunsMillis进行,如果连接为从连接池借出状态,并且未执行任何sql,并且从借出时间起已超过removeAbandonedTimeout时间,则强制归还连接到连接池中。 --> <property name="removeAbandoned" value="true" /> <!-- 超时时间,秒 --> <property name="removeAbandonedTimeout" value="80"/> <!-- 关闭abanded连接时输出错误日志,这样出现连接泄露时可以通过错误日志定位忘记关闭连接的位置 --> <property name="logAbandoned" value="true" /> <!-- 根据自身业务及事务大小来设置 --> <property name="connectionProperties" value="oracle.net.CONNECT_TIMEOUT=2000;oracle.jdbc.ReadTimeout=10000"></property> <!-- 打开PSCache,并且指定每个连接上PSCache的大小,Oracle等支持游标的数据库,打开此开关,会以数量级提升性能,具体查阅PSCache相关资料 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 配置监控统计拦截的filters --> <!-- <property name="filters" value="stat,slf4j" /> --> <property name="proxyFilters"> <list> <ref bean="log-filter" /> <ref bean="stat-filter" /> </list> </property> <!-- 配置监控统计日志的输出间隔,单位毫秒,每次输出所有统计数据会重置,酌情开启 --> <property name="timeBetweenLogStatsMillis" value="120000" /> </bean>
druid数据源检测数据库连接有效性testOnBorrow、testOnReturn、testWhileIdle属性原理分析
druid多数据源检测数据库连接的有效性属性配置如下:
#mysql默认使用ping模式,可以通过设置系统属性System.getProperties().setProperty("druid.mysql.usePingMethod", "false")更改为sql模式 #用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。默认:SELECT 1 spring.emily.datasource.config.mysql.validation-query="SELECT 1" #单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法,默认:-1 spring.emily.datasource.config.mysql.validation-query-timeout=-1 #申请连接时执行validationQuery检测连接是否有效,这个配置会降低性能。默认:false(如果test-on-borrow为true,那么test-while-idle无效) spring.emily.datasource.config.mysql.test-on-borrow=false #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。默认:true spring.emily.datasource.config.mysql.test-while-idle=true #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。默认:false spring.emily.datasource.config.mysql.test-on-return=false
获取数据库连接DruidPooledConnection对象入口代码com.alibaba.druid.pool.DruidDataSource#getConnection()
1 @Override 2 public DruidPooledConnection getConnection() throws SQLException { 3 return getConnection(maxWait); 4 } 5 6 public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { 7 //对数据库连接池进行初始化 8 init(); 9 10 if (filters.size() > 0) { 11 FilterChainImpl filterChain = new FilterChainImpl(this); 12 return filterChain.dataSource_connect(this, maxWaitMillis); 13 } else { 14 //获取数据库连接对象 15 return getConnectionDirect(maxWaitMillis); 16 } 17 }
com.alibaba.druid.pool.DruidDataSource#init初始化方法会对ValidConnectionChecker接口的实现类进行初始化
1 private void initValidConnectionChecker() { 2 if (this.validConnectionChecker != null) { 3 return; 4 } 5 6 String realDriverClassName = driver.getClass().getName(); 7 if (JdbcUtils.isMySqlDriver(realDriverClassName)) { 8 this.validConnectionChecker = new MySqlValidConnectionChecker(); 9 10 } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER) 11 || realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) { 12 this.validConnectionChecker = new OracleValidConnectionChecker(); 13 14 } else if (realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER) 15 || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_SQLJDBC4) 16 || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_JTDS)) { 17 this.validConnectionChecker = new MSSQLValidConnectionChecker(); 18 19 } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER) 20 || realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER) 21 || realDriverClassName.equals(JdbcConstants.POLARDB_DRIVER)) { 22 this.validConnectionChecker = new PGValidConnectionChecker(); 23 } 24 }
此方法分别对不同种类的数据库连接检测有效性的实现类进行初始化,接下来会在建立连接、归还链接过程中用到;
com.alibaba.druid.pool.DruidDataSource#getConnectionDirect方法是获取数据库连接的具体实现:
1 public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { 2 int notFullTimeoutRetryCnt = 0; 3 for (;;) { 4 // handle notFullTimeoutRetry 5 DruidPooledConnection poolableConnection; 6 try { 7 //从数据库连接池中获取连接对象句柄 8 poolableConnection = getConnectionInternal(maxWaitMillis); 9 } catch (GetConnectionTimeoutException ex) { 10 if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) { 11 notFullTimeoutRetryCnt++; 12 if (LOG.isWarnEnabled()) { 13 LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt); 14 } 15 continue; 16 } 17 throw ex; 18 } 19 //如果开启了获取连接有效性检查,则进行有效性检查 20 if (testOnBorrow) { 21 //有效性检查 22 boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn); 23 if (!validate) { 24 if (LOG.isDebugEnabled()) { 25 LOG.debug("skip not validate connection."); 26 } 27 28 discardConnection(poolableConnection.holder); 29 continue; 30 } 31 } else { 32 if (poolableConnection.conn.isClosed()) { 33 discardConnection(poolableConnection.holder); // 传入null,避免重复关闭 34 continue; 35 } 36 //如果testOnBorrow为false,且testWhileIdle为ture,则在判定连接在空闲时间大于timeBetweenEvictionRunsMillis(心跳检查时间)时才做判定,对性能损耗几乎为0 37 if (testWhileIdle) { 38 final DruidConnectionHolder holder = poolableConnection.holder; 39 long currentTimeMillis = System.currentTimeMillis(); 40 long lastActiveTimeMillis = holder.lastActiveTimeMillis; 41 long lastExecTimeMillis = holder.lastExecTimeMillis; 42 long lastKeepTimeMillis = holder.lastKeepTimeMillis; 43 44 if (checkExecuteTime 45 && lastExecTimeMillis != lastActiveTimeMillis) { 46 lastActiveTimeMillis = lastExecTimeMillis; 47 } 48 49 if (lastKeepTimeMillis > lastActiveTimeMillis) { 50 lastActiveTimeMillis = lastKeepTimeMillis; 51 } 52 53 long idleMillis = currentTimeMillis - lastActiveTimeMillis; 54 55 long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis; 56 57 if (timeBetweenEvictionRunsMillis <= 0) { 58 timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; 59 } 60 //连接空闲时间大于等于守护线程中心跳间隔时间时才进行有效性检查 61 if (idleMillis >= timeBetweenEvictionRunsMillis 62 || idleMillis < 0 // unexcepted branch 63 ) { 64 boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn); 65 if (!validate) { 66 if (LOG.isDebugEnabled()) { 67 LOG.debug("skip not validate connection."); 68 } 69 70 discardConnection(poolableConnection.holder); 71 continue; 72 } 73 } 74 } 75 } 76 77 if (removeAbandoned) { 78 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 79 poolableConnection.connectStackTrace = stackTrace; 80 poolableConnection.setConnectedTimeNano(); 81 poolableConnection.traceEnable = true; 82 83 activeConnectionLock.lock(); 84 try { 85 activeConnections.put(poolableConnection, PRESENT); 86 } finally { 87 activeConnectionLock.unlock(); 88 } 89 } 90 91 if (!this.defaultAutoCommit) { 92 poolableConnection.setAutoCommit(false); 93 } 94 95 return poolableConnection; 96 } 97 }
com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal(com.alibaba.druid.pool.DruidConnectionHolder, java.sql.Connection)方法对从数据库中获取到的连接有效性进行检查
1 protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) { 2 String sqlFile = JdbcSqlStat.getContextSqlFile(); 3 String sqlName = JdbcSqlStat.getContextSqlName(); 4 5 if (sqlFile != null) { 6 JdbcSqlStat.setContextSqlFile(null); 7 } 8 if (sqlName != null) { 9 JdbcSqlStat.setContextSqlName(null); 10 } 11 try { 12 //validConnectionChecker属性是init方法中对各数据库驱动进行初始化的有效性检查实现类 13 if (validConnectionChecker != null) { 14 //对连接进行有效性检查 15 boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout); 16 ... 17 }
对mysql数据源进行连接有效性检查实现类MySqlValidConnectionChecker源码分析
1 public class MySqlValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable { 2 3 public static final int DEFAULT_VALIDATION_QUERY_TIMEOUT = 1; 4 public static final String DEFAULT_VALIDATION_QUERY = "SELECT 1"; 5 6 private static final long serialVersionUID = 1L; 7 private static final Log LOG = LogFactory.getLog(MySqlValidConnectionChecker.class); 8 9 private Class<?> clazz; 10 private Method ping; 11 12 private boolean usePingMethod = false; 13 14 public MySqlValidConnectionChecker(){ 15 try { 16 //8.0之前的mysql驱动 17 clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection"); 18 if (clazz == null) { 19 //8.0之后的驱动 20 clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl"); 21 } 22 23 if (clazz != null) { 24 //获取pingInternal方法示例 25 ping = clazz.getMethod("pingInternal", boolean.class, int.class); 26 } 27 28 if (ping != null) { 29 //实际是使用ping模式检测连接有效性 30 usePingMethod = true; 31 } 32 } catch (Exception e) { 33 LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e); 34 } 35 36 configFromProperties(System.getProperties()); 37 } 38 39 @Override 40 public void configFromProperties(Properties properties) { 41 if (properties == null) { 42 return; 43 } 44 //根据系统属性设置判定连接有效性的方法 45 String property = properties.getProperty("druid.mysql.usePingMethod"); 46 if ("true".equals(property)) { 47 setUsePingMethod(true); 48 } else if ("false".equals(property)) { 49 setUsePingMethod(false); 50 } 51 } 52 53 public boolean isUsePingMethod() { 54 return usePingMethod; 55 } 56 57 public void setUsePingMethod(boolean usePingMethod) { 58 this.usePingMethod = usePingMethod; 59 } 60 61 public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception { 62 if (conn.isClosed()) { 63 return false; 64 } 65 66 if (usePingMethod) { 67 if (conn instanceof DruidPooledConnection) { 68 conn = ((DruidPooledConnection) conn).getConnection(); 69 } 70 71 if (conn instanceof ConnectionProxy) { 72 conn = ((ConnectionProxy) conn).getRawObject(); 73 } 74 75 if (clazz.isAssignableFrom(conn.getClass())) { 76 if (validationQueryTimeout <= 0) { 77 validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT; 78 } 79 80 try { 81 //使用ping的方式判定连接有效性 82 ping.invoke(conn, true, validationQueryTimeout * 1000); 83 } catch (InvocationTargetException e) { 84 Throwable cause = e.getCause(); 85 if (cause instanceof SQLException) { 86 throw (SQLException) cause; 87 } 88 throw e; 89 } 90 return true; 91 } 92 } 93 94 String query = validateQuery; 95 if (validateQuery == null || validateQuery.isEmpty()) { 96 query = DEFAULT_VALIDATION_QUERY; 97 } 98 99 Statement stmt = null; 100 ResultSet rs = null; 101 try { 102 103 //使用sql的方式校验连接的有效性 104 stmt = conn.createStatement(); 105 if (validationQueryTimeout > 0) { 106 stmt.setQueryTimeout(validationQueryTimeout); 107 } 108 rs = stmt.executeQuery(query); 109 return true; 110 } finally { 111 JdbcUtils.close(rs); 112 JdbcUtils.close(stmt); 113 } 114 115 } 116 117 }
默认情况下是使用ping的方式判定连接的有效性,但是可以通过druid.mysql.usePingMethod系统属性修改为sql判定连接有效性模式。
com.alibaba.druid.pool.vendor.OracleValidConnectionChecker是oracle校验连接有效性的实现类
1 public class OracleValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable { 2 3 private static final long serialVersionUID = -2227528634302168877L; 4 5 6 private int timeout = 1; 7 8 private String defaultValidateQuery = "SELECT 'x' FROM DUAL"; 9 10 public OracleValidConnectionChecker(){ 11 configFromProperties(System.getProperties()); 12 } 13 14 @Override 15 public void configFromProperties(Properties properties) { 16 if (properties == null) { 17 return; 18 } 19 //可以通过系统属性方式修改超时时间 20 String property = properties.getProperty("druid.oracle.pingTimeout"); 21 if (property != null && property.length() > 0) { 22 int value = Integer.parseInt(property); 23 setTimeout(value); 24 } 25 } 26 27 public void setTimeout(int seconds) { 28 this.timeout = seconds; 29 } 30 //采用sql模式校验有效性 31 public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception { 32 if (validateQuery == null || validateQuery.isEmpty()) { 33 validateQuery = this.defaultValidateQuery; 34 } 35 36 if (conn.isClosed()) { 37 return false; 38 } 39 40 if (conn instanceof DruidPooledConnection) { 41 conn = ((DruidPooledConnection) conn).getConnection(); 42 } 43 44 if (conn instanceof ConnectionProxy) { 45 conn = ((ConnectionProxy) conn).getRawObject(); 46 } 47 48 if (validateQuery == null || validateQuery.isEmpty()) { 49 return true; 50 } 51 52 int queryTimeout = validationQueryTimeout <= 0 ? timeout : validationQueryTimeout; 53 54 Statement stmt = null; 55 ResultSet rs = null; 56 try { 57 stmt = conn.createStatement(); 58 stmt.setQueryTimeout(queryTimeout); 59 rs = stmt.executeQuery(validateQuery); 60 return true; 61 } finally { 62 JdbcUtils.close(rs); 63 JdbcUtils.close(stmt); 64 } 65 } 66 }
oracle采用的是sql校验连接有效性的模式,默认超时时间是1s,可以通过系统属性druid.oracle.pingTimeout修改超时时间,也可以通过validation-query-timeout属性配置修改超时时间。
另外连接池会在com.alibaba.druid.pool.DruidDataSource#recycle方法中回收连接的时候通过属性testOnReturn判定是否需要判定连接的有效性。
销毁连接分析
在创建连接的init()
方法,注意到创建了线程DestroyConnectionThread
用来销毁连接的,线程循环是通过timeBetweenEvictionRunsMillis
参数来设置连接检查的时间间隔。
1 public void run() { 2 //计数器减一 3 initedLatch.countDown(); 4 5 for (;;) { 6 // 从前面开始删除 7 try { 8 //如果连接池未关闭 9 if (closed || closing) { 10 break; 11 } 12 //设置时间间隔休眠 13 if (timeBetweenEvictionRunsMillis > 0) { 14 Thread.sleep(timeBetweenEvictionRunsMillis); 15 } else { 16 Thread.sleep(1000); 17 } 18 //如果发生中断异常则退出 19 if (Thread.interrupted()) { 20 break; 21 } 22 //运行销毁任务 23 destroyTask.run(); 24 } catch (InterruptedException e) { 25 break; 26 } 27 } 28 }
DestroyTask
销毁任务类,通过DestroyConnectionThread
线程直接调用,一旦有自定义销毁任务时,才会变为线程执行。
1 @Override 2 public void run() { 3 //检查连接 4 shrink(true, keepAlive); 5 //移出废弃的连接 6 if (isRemoveAbandoned()) { 7 removeAbandoned(); 8 } 9 }
shrink
该方法主要是检查连接,并销毁连接。
1 public void shrink(boolean checkTime, boolean keepAlive) { 2 //获取锁 3 try { 4 lock.lockInterruptibly(); 5 } catch (InterruptedException e) { 6 return; 7 } 8 9 boolean needFill = false; 10 int evictCount = 0; 11 int keepAliveCount = 0; 12 //计算错误增量 13 int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink; 14 fatalErrorCountLastShrink = fatalErrorCount; 15 16 try { 17 //如果还未进行初始化,则进行init() 18 if (!inited) { 19 return; 20 } 21 //剩余空闲连接数 = 连接池中连接数- 最小空闲连接数 22 final int checkCount = poolingCount - minIdle; 23 final long currentTimeMillis = System.currentTimeMillis(); 24 for (int i = 0; i < poolingCount; ++i) { 25 //从连接池中拿连接 26 DruidConnectionHolder connection = connections[i]; 27 //(如果发生致命错误或者致命错误次数大于0)&& 最后致命错误时间大于当前连接的连接时间 28 if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) { 29 //设置为存活连接 30 keepAliveConnections[keepAliveCount++] = connection; 31 continue; 32 } 33 34 if (checkTime) { 35 //如果设置连接使用销毁时间,则判断当前连接使用时间是否大于这个时间,如果大于则直接将连接放入销毁的连接池中 36 if (phyTimeoutMillis > 0) { 37 long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis; 38 if (phyConnectTimeMillis > phyTimeoutMillis) { 39 evictConnections[evictCount++] = connection; 40 continue; 41 } 42 } 43 //计算空闲时间 44 long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis; 45 //空闲时间 < 最小存活时间 && 空闲时间 < 保证活跃时间,退出循环检查 46 if (idleMillis < minEvictableIdleTimeMillis 47 && idleMillis < keepAliveBetweenTimeMillis 48 ) { 49 break; 50 } 51 //空闲时间 >= 最小存活时间,将连接放入销毁池中 52 if (idleMillis >= minEvictableIdleTimeMillis) { 53 if (checkTime && i < checkCount) { 54 evictConnections[evictCount++] = connection; 55 continue; 56 } else if (idleMillis > maxEvictableIdleTimeMillis) { 57 evictConnections[evictCount++] = connection; 58 continue; 59 } 60 } 61 //开启保活机制 && 空闲时间 >= 连接保活时间,将连接放入存活池中。 62 if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) { 63 keepAliveConnections[keepAliveCount++] = connection; 64 } 65 //如果未开启检查,则仅留下最小空闲连接。 66 } else { 67 if (i < checkCount) { 68 evictConnections[evictCount++] = connection; 69 } else { 70 break; 71 } 72 } 73 } 74 //删除数量 = 销毁连接数量+ 活跃连接数量 75 int removeCount = evictCount + keepAliveCount; 76 if (removeCount > 0) { 77 System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); 78 Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); 79 //空闲连接池数量 = 空闲连接池数量-删除数量 80 poolingCount -= removeCount; 81 } 82 //连接池检查数量 = 连接池检查数量 + 活跃连接数量 83 keepAliveCheckCount += keepAliveCount; 84 // 85 if (keepAlive && poolingCount + activeCount < minIdle) { 86 needFill = true; 87 } 88 } finally { 89 lock.unlock(); 90 } 91 //如果销毁数量大于0,则关闭这些连接 92 if (evictCount > 0) { 93 for (int i = 0; i < evictCount; ++i) { 94 DruidConnectionHolder item = evictConnections[i]; 95 Connection connection = item.getConnection(); 96 JdbcUtils.close(connection); 97 destroyCountUpdater.incrementAndGet(this); 98 } 99 Arrays.fill(evictConnections, null); 100 } 101 //如果活跃连接数量 >0 ,则对连接进行校验 102 if (keepAliveCount > 0) { 103 // keep order 104 for (int i = keepAliveCount - 1; i >= 0; --i) { 105 DruidConnectionHolder holer = keepAliveConnections[i]; 106 Connection connection = holer.getConnection(); 107 holer.incrementKeepAliveCheckCount(); 108 109 boolean validate = false; 110 try { 111 this.validateConnection(connection); 112 validate = true; 113 } catch (Throwable error) { 114 if (LOG.isDebugEnabled()) { 115 LOG.debug("keepAliveErr", error); 116 } 117 // skip 118 } 119 120 boolean discard = !validate; 121 if (validate) { 122 holer.lastKeepTimeMillis = System.currentTimeMillis(); 123 boolean putOk = put(holer, 0L, true); 124 if (!putOk) { 125 discard = true; 126 } 127 } 128 //如果连接不可用,则关闭 129 if (discard) { 130 try { 131 connection.close(); 132 } catch (Exception e) { 133 // skip 134 } 135 136 lock.lock(); 137 try { 138 discardCount++; 139 140 if (activeCount + poolingCount <= minIdle) { 141 emptySignal(); 142 } 143 } finally { 144 lock.unlock(); 145 } 146 } 147 } 148 this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount); 149 Arrays.fill(keepAliveConnections, null); 150 } 151 //如果需要创建连接,则释放锁进行创建 152 if (needFill) { 153 lock.lock(); 154 try { 155 int fillCount = minIdle - (activeCount + poolingCount + createTaskCount); 156 for (int i = 0; i < fillCount; ++i) { 157 emptySignal(); 158 } 159 } finally { 160 lock.unlock(); 161 } 162 } else if (onFatalError || fatalErrorIncrement > 0) { 163 lock.lock(); 164 try { 165 emptySignal(); 166 } finally { 167 lock.unlock(); 168 } 169 } 170 }
标签:driud,int,连接,connection,调优,null,public,conn From: https://www.cnblogs.com/fnlingnzb-learner/p/16985913.html