0


HikariCP实战 | 通过查看源码分析如何解决maxLifeTime配置问题

目录

在这里插入图片描述

1、追本溯源

很多年前在stackoverflow上写过一篇文章:
https://stackoverflow.com/questions/28180562/hikaricp-and-maxlifetime#

在这里插入图片描述
hikariCP是非常优秀的JDBC connection pool. 官方配置:https://github.com/brettwooldridge/HikariCP

2、解决hikariCP的maxLifetime配置出现以下warn问题

WARN com.zaxxer.hikari.HikariConfig - maxLifetime is less than 120000ms, using default 1800000ms.

3、具体解决步骤(查看源码)

i don’t know your HikariCP Version, but in the version 2.2.4 you will find the reason why it will throw the above warning.

HikariConfig.class

(in the

com.zaxxer.hikari.HikariConfig

):

 private void More ...validateNumerics()
  {
     Logger logger = LoggerFactory.getLogger(getClass());

     if (connectionTimeout == Integer.MAX_VALUE) {
        logger.warn("No connection wait timeout is set, this might cause an infinite wait.");
     }

     if (minIdle < 0 || minIdle > maxPoolSize) {
        minIdle = maxPoolSize;
     }

     if (maxLifetime < 0) {
        logger.error("maxLifetime cannot be negative.");
        throw new IllegalArgumentException("maxLifetime cannot be negative.");
     }
     else if (maxLifetime > 0 && maxLifetime < TimeUnit.SECONDS.toMillis(120)) {
        logger.warn("maxLifetime is less than 120000ms, using default {}ms.", MAX_LIFETIME);
        maxLifetime = MAX_LIFETIME;
     }

     if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(30)) {
        logger.warn("idleTimeout is less than 30000ms, using default {}ms.", IDLE_TIMEOUT);
        idleTimeout = IDLE_TIMEOUT;
     }
     else if (idleTimeout > maxLifetime && maxLifetime > 0) {
        logger.warn("idleTimeout is greater than maxLifetime, setting to maxLifetime.");
        idleTimeout = maxLifetime;
     }

from this code, the maxLifeTime is at least 120000ms, using default 1800000ms. so you can’t set the maxLifeTime to 30000ms(30*1000). I guess your HikariCP version is at least older than 2.2.4.

But when you find the latest HikariCP version 2.7.4. it said “We strongly recommend setting this value, and it should be at least 30 seconds less than any database or infrastructure imposed connection time limit.”

the same class HikariConfig.class:

private void validateNumerics() {
    if(this.maxLifetime != 0L && this.maxLifetime < TimeUnit.SECONDS.toMillis(30L)) {
        LOGGER.warn("{} - maxLifetime is less than 30000ms, setting to default {}ms.", this.poolName, Long.valueOf(MAX_LIFETIME));
        this.maxLifetime = MAX_LIFETIME;
    }

    if(this.idleTimeout + TimeUnit.SECONDS.toMillis(1L) > this.maxLifetime && this.maxLifetime > 0L) {
        LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", this.poolName);
        this.idleTimeout = 0L;
    }

    if(this.idleTimeout != 0L && this.idleTimeout < TimeUnit.SECONDS.toMillis(10L)) {
        LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", this.poolName, Long.valueOf(IDLE_TIMEOUT));
        this.idleTimeout = IDLE_TIMEOUT;
    }

    if(this.leakDetectionThreshold > 0L && !unitTest && (this.leakDetectionThreshold < TimeUnit.SECONDS.toMillis(2L) || this.leakDetectionThreshold > this.maxLifetime && this.maxLifetime > 0L)) {
        LOGGER.warn("{} - leakDetectionThreshold is less than 2000ms or more than maxLifetime, disabling it.", this.poolName);
        this.leakDetectionThreshold = 0L;
    }

    if(this.connectionTimeout < 250L) {
        LOGGER.warn("{} - connectionTimeout is less than 250ms, setting to {}ms.", this.poolName, Long.valueOf(CONNECTION_TIMEOUT));
        this.connectionTimeout = CONNECTION_TIMEOUT;
    }

    if(this.validationTimeout < 250L) {
        LOGGER.warn("{} - validationTimeout is less than 250ms, setting to {}ms.", this.poolName, Long.valueOf(VALIDATION_TIMEOUT));
        this.validationTimeout = VALIDATION_TIMEOUT;
    }

    if(this.maxPoolSize < 1) {
        this.maxPoolSize = this.minIdle <= 0?10:this.minIdle;
    }

    if(this.minIdle < 0 || this.minIdle > this.maxPoolSize) {
        this.minIdle = this.maxPoolSize;
    }

}

完整代码看这里:
https://github.com/brettwooldridge/HikariCP/blob/dev/src/main/java/com/zaxxer/hikari/HikariConfig.java

/*
 * Copyright (C) 2013, 2014 Brett Wooldridge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */packagecom.zaxxer.hikari;importcom.codahale.metrics.health.HealthCheckRegistry;importcom.zaxxer.hikari.metrics.MetricsTrackerFactory;importcom.zaxxer.hikari.util.PropertyElf;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjavax.naming.InitialContext;importjavax.naming.NamingException;importjavax.sql.DataSource;importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.lang.reflect.Modifier;importjava.security.AccessControlException;importjava.sql.Connection;importjava.util.Properties;importjava.util.TreeSet;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.ThreadFactory;importjava.util.concurrent.ThreadLocalRandom;importstaticcom.zaxxer.hikari.util.UtilityElf.getNullIfEmpty;importstaticcom.zaxxer.hikari.util.UtilityElf.safeIsAssignableFrom;importstaticjava.util.concurrent.TimeUnit.MINUTES;importstaticjava.util.concurrent.TimeUnit.SECONDS;@SuppressWarnings({"SameParameterValue","unused"})publicclassHikariConfigimplementsHikariConfigMXBean{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(HikariConfig.class);privatestaticfinalchar[]ID_CHARACTERS="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();privatestaticfinallongCONNECTION_TIMEOUT=SECONDS.toMillis(30);privatestaticfinallongVALIDATION_TIMEOUT=SECONDS.toMillis(5);privatestaticfinallongSOFT_TIMEOUT_FLOOR=Long.getLong("com.zaxxer.hikari.timeoutMs.floor",250L);privatestaticfinallongIDLE_TIMEOUT=MINUTES.toMillis(10);privatestaticfinallongMAX_LIFETIME=MINUTES.toMillis(30);privatestaticfinallongDEFAULT_KEEPALIVE_TIME=0L;privatestaticfinalintDEFAULT_POOL_SIZE=10;privatestaticboolean unitTest =false;// Properties changeable at runtime through the HikariConfigMXBean//privatevolatileString catalog;privatevolatilelong connectionTimeout;privatevolatilelong validationTimeout;privatevolatilelong idleTimeout;privatevolatilelong leakDetectionThreshold;privatevolatilelong maxLifetime;privatevolatileint maxPoolSize;privatevolatileint minIdle;privatevolatileString username;privatevolatileString password;// Properties NOT changeable at runtime//privatelong initializationFailTimeout;privateString connectionInitSql;privateString connectionTestQuery;privateString dataSourceClassName;privateString dataSourceJndiName;privateString driverClassName;privateString exceptionOverrideClassName;privateString jdbcUrl;privateString poolName;privateString schema;privateString transactionIsolationName;privateboolean isAutoCommit;privateboolean isReadOnly;privateboolean isIsolateInternalQueries;privateboolean isRegisterMbeans;privateboolean isAllowPoolSuspension;privateDataSource dataSource;privateProperties dataSourceProperties;privateThreadFactory threadFactory;privateScheduledExecutorService scheduledExecutor;privateMetricsTrackerFactory metricsTrackerFactory;privateObject metricRegistry;privateObject healthCheckRegistry;privateProperties healthCheckProperties;privatelong keepaliveTime;privatevolatilebooleansealed;/**
    * Default constructor
    * <p>
    * If the System property {@code hikari.configurationFile} is set,
    * then the default constructor will attempt to load the specified configuration file
    * <p>
    * {@link #HikariConfig(String propertyFileName)} can be similarly used
    * instead of using the system property
    */publicHikariConfig(){
      dataSourceProperties =newProperties();
      healthCheckProperties =newProperties();

      minIdle =-1;
      maxPoolSize =-1;
      maxLifetime =MAX_LIFETIME;
      connectionTimeout =CONNECTION_TIMEOUT;
      validationTimeout =VALIDATION_TIMEOUT;
      idleTimeout =IDLE_TIMEOUT;
      initializationFailTimeout =1;
      isAutoCommit =true;
      keepaliveTime =DEFAULT_KEEPALIVE_TIME;var systemProp =System.getProperty("hikaricp.configurationFile");if(systemProp !=null){loadProperties(systemProp);}}/**
    * Construct a HikariConfig from the specified properties object.
    *
    * @param properties the name of the property file
    */publicHikariConfig(Properties properties){this();PropertyElf.setTargetFromProperties(this, properties);}/**
    * Construct a HikariConfig from the specified property file name.  <code>propertyFileName</code>
    * will first be treated as a path in the file-system, and if that fails the
    * Class.getResourceAsStream(propertyFileName) will be tried.
    *
    * @param propertyFileName the name of the property file
    */publicHikariConfig(String propertyFileName){this();loadProperties(propertyFileName);}// ***********************************************************************//                       HikariConfigMXBean methods// ***********************************************************************/** {@inheritDoc} */@OverridepublicStringgetCatalog(){return catalog;}/** {@inheritDoc} */@OverridepublicvoidsetCatalog(String catalog){this.catalog = catalog;}/** {@inheritDoc} */@OverridepubliclonggetConnectionTimeout(){return connectionTimeout;}/** {@inheritDoc} */@OverridepublicvoidsetConnectionTimeout(long connectionTimeoutMs){if(connectionTimeoutMs ==0){this.connectionTimeout =Integer.MAX_VALUE;}elseif(connectionTimeoutMs <SOFT_TIMEOUT_FLOOR){thrownewIllegalArgumentException("connectionTimeout cannot be less than "+SOFT_TIMEOUT_FLOOR+"ms");}else{this.connectionTimeout = connectionTimeoutMs;}}/** {@inheritDoc} */@OverridepubliclonggetIdleTimeout(){return idleTimeout;}/** {@inheritDoc} */@OverridepublicvoidsetIdleTimeout(long idleTimeoutMs){if(idleTimeoutMs <0){thrownewIllegalArgumentException("idleTimeout cannot be negative");}this.idleTimeout = idleTimeoutMs;}/** {@inheritDoc} */@OverridepubliclonggetLeakDetectionThreshold(){return leakDetectionThreshold;}/** {@inheritDoc} */@OverridepublicvoidsetLeakDetectionThreshold(long leakDetectionThresholdMs){this.leakDetectionThreshold = leakDetectionThresholdMs;}/** {@inheritDoc} */@OverridepubliclonggetMaxLifetime(){return maxLifetime;}/** {@inheritDoc} */@OverridepublicvoidsetMaxLifetime(long maxLifetimeMs){this.maxLifetime = maxLifetimeMs;}/** {@inheritDoc} */@OverridepublicintgetMaximumPoolSize(){return maxPoolSize;}/** {@inheritDoc} */@OverridepublicvoidsetMaximumPoolSize(int maxPoolSize){if(maxPoolSize <1){thrownewIllegalArgumentException("maxPoolSize cannot be less than 1");}this.maxPoolSize = maxPoolSize;}/** {@inheritDoc} */@OverridepublicintgetMinimumIdle(){return minIdle;}/** {@inheritDoc} */@OverridepublicvoidsetMinimumIdle(int minIdle){if(minIdle <0){thrownewIllegalArgumentException("minimumIdle cannot be negative");}this.minIdle = minIdle;}/**
    * Get the default password to use for DataSource.getConnection(username, password) calls.
    * @return the password
    */publicStringgetPassword(){return password;}/**
    * Set the default password to use for DataSource.getConnection(username, password) calls.
    * @param password the password
    */@OverridepublicvoidsetPassword(String password){this.password = password;}/**
    * Get the default username used for DataSource.getConnection(username, password) calls.
    *
    * @return the username
    */publicStringgetUsername(){return username;}/**
    * Set the default username used for DataSource.getConnection(username, password) calls.
    *
    * @param username the username
    */@OverridepublicvoidsetUsername(String username){this.username = username;}/** {@inheritDoc} */@OverridepubliclonggetValidationTimeout(){return validationTimeout;}/** {@inheritDoc} */@OverridepublicvoidsetValidationTimeout(long validationTimeoutMs){if(validationTimeoutMs <SOFT_TIMEOUT_FLOOR){thrownewIllegalArgumentException("validationTimeout cannot be less than "+SOFT_TIMEOUT_FLOOR+"ms");}this.validationTimeout = validationTimeoutMs;}// ***********************************************************************//                     All other configuration methods// ***********************************************************************/**
    * Get the SQL query to be executed to test the validity of connections.
    *
    * @return the SQL query string, or null
    */publicStringgetConnectionTestQuery(){return connectionTestQuery;}/**
    * Set the SQL query to be executed to test the validity of connections. Using
    * the JDBC4 <code>Connection.isValid()</code> method to test connection validity can
    * be more efficient on some databases and is recommended.
    *
    * @param connectionTestQuery a SQL query string
    */publicvoidsetConnectionTestQuery(String connectionTestQuery){checkIfSealed();this.connectionTestQuery = connectionTestQuery;}/**
    * Get the SQL string that will be executed on all new connections when they are
    * created, before they are added to the pool.
    *
    * @return the SQL to execute on new connections, or null
    */publicStringgetConnectionInitSql(){return connectionInitSql;}/**
    * Set the SQL string that will be executed on all new connections when they are
    * created, before they are added to the pool.  If this query fails, it will be
    * treated as a failed connection attempt.
    *
    * @param connectionInitSql the SQL to execute on new connections
    */publicvoidsetConnectionInitSql(String connectionInitSql){checkIfSealed();this.connectionInitSql = connectionInitSql;}/**
    * Get the {@link DataSource} that has been explicitly specified to be wrapped by the
    * pool.
    *
    * @return the {@link DataSource} instance, or null
    */publicDataSourcegetDataSource(){return dataSource;}/**
    * Set a {@link DataSource} for the pool to explicitly wrap.  This setter is not
    * available through property file based initialization.
    *
    * @param dataSource a specific {@link DataSource} to be wrapped by the pool
    */publicvoidsetDataSource(DataSource dataSource){checkIfSealed();this.dataSource = dataSource;}/**
    * Get the name of the JDBC {@link DataSource} class used to create Connections.
    *
    * @return the fully qualified name of the JDBC {@link DataSource} class
    */publicStringgetDataSourceClassName(){return dataSourceClassName;}/**
    * Set the fully qualified class name of the JDBC {@link DataSource} that will be used create Connections.
    *
    * @param className the fully qualified name of the JDBC {@link DataSource} class
    */publicvoidsetDataSourceClassName(String className){checkIfSealed();this.dataSourceClassName = className;}/**
    * Add a property (name/value pair) that will be used to configure the {@link DataSource}/{@link java.sql.Driver}.
    *
    * In the case of a {@link DataSource}, the property names will be translated to Java setters following the Java Bean
    * naming convention.  For example, the property {@code cachePrepStmts} will translate into {@code setCachePrepStmts()}
    * with the {@code value} passed as a parameter.
    *
    * In the case of a {@link java.sql.Driver}, the property will be added to a {@link Properties} instance that will
    * be passed to the driver during {@link java.sql.Driver#connect(String, Properties)} calls.
    *
    * @param propertyName the name of the property
    * @param value the value to be used by the DataSource/Driver
    */publicvoidaddDataSourceProperty(String propertyName,Object value){checkIfSealed();
      dataSourceProperties.put(propertyName, value);}publicStringgetDataSourceJNDI(){returnthis.dataSourceJndiName;}publicvoidsetDataSourceJNDI(String jndiDataSource){checkIfSealed();this.dataSourceJndiName = jndiDataSource;}publicPropertiesgetDataSourceProperties(){return dataSourceProperties;}publicvoidsetDataSourceProperties(Properties dsProperties){checkIfSealed();
      dataSourceProperties.putAll(dsProperties);}publicStringgetDriverClassName(){return driverClassName;}publicvoidsetDriverClassName(String driverClassName){checkIfSealed();var driverClass =attemptFromContextLoader(driverClassName);try{if(driverClass ==null){
            driverClass =this.getClass().getClassLoader().loadClass(driverClassName);LOGGER.debug("Driver class {} found in the HikariConfig class classloader {}", driverClassName,this.getClass().getClassLoader());}}catch(ClassNotFoundException e){LOGGER.error("Failed to load driver class {} from HikariConfig class classloader {}", driverClassName,this.getClass().getClassLoader());}if(driverClass ==null){thrownewRuntimeException("Failed to load driver class "+ driverClassName +" in either of HikariConfig class loader or Thread context classloader");}try{
         driverClass.getConstructor().newInstance();this.driverClassName = driverClassName;}catch(Exception e){thrownewRuntimeException("Failed to instantiate class "+ driverClassName, e);}}publicStringgetJdbcUrl(){return jdbcUrl;}publicvoidsetJdbcUrl(String jdbcUrl){checkIfSealed();this.jdbcUrl = jdbcUrl;}/**
    * Get the default auto-commit behavior of connections in the pool.
    *
    * @return the default auto-commit behavior of connections
    */publicbooleanisAutoCommit(){return isAutoCommit;}/**
    * Set the default auto-commit behavior of connections in the pool.
    *
    * @param isAutoCommit the desired auto-commit default for connections
    */publicvoidsetAutoCommit(boolean isAutoCommit){checkIfSealed();this.isAutoCommit = isAutoCommit;}/**
    * Get the pool suspension behavior (allowed or disallowed).
    *
    * @return the pool suspension behavior
    */publicbooleanisAllowPoolSuspension(){return isAllowPoolSuspension;}/**
    * Set whether or not pool suspension is allowed.  There is a performance
    * impact when pool suspension is enabled.  Unless you need it (for a
    * redundancy system for example) do not enable it.
    *
    * @param isAllowPoolSuspension the desired pool suspension allowance
    */publicvoidsetAllowPoolSuspension(boolean isAllowPoolSuspension){checkIfSealed();this.isAllowPoolSuspension = isAllowPoolSuspension;}/**
    * Get the pool initialization failure timeout.  See {@code #setInitializationFailTimeout(long)}
    * for details.
    *
    * @return the number of milliseconds before the pool initialization fails
    * @see HikariConfig#setInitializationFailTimeout(long)
    */publiclonggetInitializationFailTimeout(){return initializationFailTimeout;}/**
    * Set the pool initialization failure timeout.  This setting applies to pool
    * initialization when {@link HikariDataSource} is constructed with a {@link HikariConfig},
    * or when {@link HikariDataSource} is constructed using the no-arg constructor
    * and {@link HikariDataSource#getConnection()} is called.
    * <ul>
    *   <li>Any value greater than zero will be treated as a timeout for pool initialization.
    *       The calling thread will be blocked from continuing until a successful connection
    *       to the database, or until the timeout is reached.  If the timeout is reached, then
    *       a {@code PoolInitializationException} will be thrown. </li>
    *   <li>A value of zero will <i>not</i>  prevent the pool from starting in the
    *       case that a connection cannot be obtained. However, upon start the pool will
    *       attempt to obtain a connection and validate that the {@code connectionTestQuery}
    *       and {@code connectionInitSql} are valid.  If those validations fail, an exception
    *       will be thrown.  If a connection cannot be obtained, the validation is skipped
    *       and the the pool will start and continue to try to obtain connections in the
    *       background.  This can mean that callers to {@code DataSource#getConnection()} may
    *       encounter exceptions. </li>
    *   <li>A value less than zero will bypass any connection attempt and validation during
    *       startup, and therefore the pool will start immediately.  The pool will continue to
    *       try to obtain connections in the background. This can mean that callers to
    *       {@code DataSource#getConnection()} may encounter exceptions. </li>
    * </ul>
    * Note that if this timeout value is greater than or equal to zero (0), and therefore an
    * initial connection validation is performed, this timeout does not override the
    * {@code connectionTimeout} or {@code validationTimeout}; they will be honored before this
    * timeout is applied.  The default value is one millisecond.
    *
    * @param initializationFailTimeout the number of milliseconds before the
    *        pool initialization fails, or 0 to validate connection setup but continue with
    *        pool start, or less than zero to skip all initialization checks and start the
    *        pool without delay.
    */publicvoidsetInitializationFailTimeout(long initializationFailTimeout){checkIfSealed();this.initializationFailTimeout = initializationFailTimeout;}/**
    * Determine whether internal pool queries, principally aliveness checks, will be isolated in their own transaction
    * via {@link Connection#rollback()}.  Defaults to {@code false}.
    *
    * @return {@code true} if internal pool queries are isolated, {@code false} if not
    */publicbooleanisIsolateInternalQueries(){return isIsolateInternalQueries;}/**
    * Configure whether internal pool queries, principally aliveness checks, will be isolated in their own transaction
    * via {@link Connection#rollback()}.  Defaults to {@code false}.
    *
    * @param isolate {@code true} if internal pool queries should be isolated, {@code false} if not
    */publicvoidsetIsolateInternalQueries(boolean isolate){checkIfSealed();this.isIsolateInternalQueries = isolate;}publicMetricsTrackerFactorygetMetricsTrackerFactory(){return metricsTrackerFactory;}publicvoidsetMetricsTrackerFactory(MetricsTrackerFactory metricsTrackerFactory){if(metricRegistry !=null){thrownewIllegalStateException("cannot use setMetricsTrackerFactory() and setMetricRegistry() together");}this.metricsTrackerFactory = metricsTrackerFactory;}/**
    * Get the MetricRegistry instance to use for registration of metrics used by HikariCP.  Default is {@code null}.
    *
    * @return the MetricRegistry instance that will be used
    */publicObjectgetMetricRegistry(){return metricRegistry;}/**
    * Set a MetricRegistry instance to use for registration of metrics used by HikariCP.
    *
    * @param metricRegistry the MetricRegistry instance to use
    */publicvoidsetMetricRegistry(Object metricRegistry){if(metricsTrackerFactory !=null){thrownewIllegalStateException("cannot use setMetricRegistry() and setMetricsTrackerFactory() together");}if(metricRegistry !=null){
         metricRegistry =getObjectOrPerformJndiLookup(metricRegistry);if(!safeIsAssignableFrom(metricRegistry,"com.codahale.metrics.MetricRegistry")&&!(safeIsAssignableFrom(metricRegistry,"io.micrometer.core.instrument.MeterRegistry"))){thrownewIllegalArgumentException("Class must be instance of com.codahale.metrics.MetricRegistry or io.micrometer.core.instrument.MeterRegistry");}}this.metricRegistry = metricRegistry;}/**
    * Get the HealthCheckRegistry that will be used for registration of health checks by HikariCP.  Currently only
    * Codahale/DropWizard is supported for health checks.
    *
    * @return the HealthCheckRegistry instance that will be used
    */publicObjectgetHealthCheckRegistry(){return healthCheckRegistry;}/**
    * Set the HealthCheckRegistry that will be used for registration of health checks by HikariCP.  Currently only
    * Codahale/DropWizard is supported for health checks.  Default is {@code null}.
    *
    * @param healthCheckRegistry the HealthCheckRegistry to be used
    */publicvoidsetHealthCheckRegistry(Object healthCheckRegistry){checkIfSealed();if(healthCheckRegistry !=null){
         healthCheckRegistry =getObjectOrPerformJndiLookup(healthCheckRegistry);if(!(healthCheckRegistry instanceofHealthCheckRegistry)){thrownewIllegalArgumentException("Class must be an instance of com.codahale.metrics.health.HealthCheckRegistry");}}this.healthCheckRegistry = healthCheckRegistry;}publicPropertiesgetHealthCheckProperties(){return healthCheckProperties;}publicvoidsetHealthCheckProperties(Properties healthCheckProperties){checkIfSealed();this.healthCheckProperties.putAll(healthCheckProperties);}publicvoidaddHealthCheckProperty(String key,String value){checkIfSealed();
      healthCheckProperties.setProperty(key, value);}/**
    * This property controls the keepalive interval for a connection in the pool. An in-use connection will never be
    * tested by the keepalive thread, only when it is idle will it be tested.
    *
    * @return the interval in which connections will be tested for aliveness, thus keeping them alive by the act of checking. Value is in milliseconds, default is 0 (disabled).
    */publiclonggetKeepaliveTime(){return keepaliveTime;}/**
    * This property controls the keepalive interval for a connection in the pool. An in-use connection will never be
    * tested by the keepalive thread, only when it is idle will it be tested.
    *
    * @param keepaliveTimeMs the interval in which connections will be tested for aliveness, thus keeping them alive by the act of checking. Value is in milliseconds, default is 0 (disabled).
    */publicvoidsetKeepaliveTime(long keepaliveTimeMs){this.keepaliveTime = keepaliveTimeMs;}/**
    * Determine whether the Connections in the pool are in read-only mode.
    *
    * @return {@code true} if the Connections in the pool are read-only, {@code false} if not
    */publicbooleanisReadOnly(){return isReadOnly;}/**
    * Configures the Connections to be added to the pool as read-only Connections.
    *
    * @param readOnly {@code true} if the Connections in the pool are read-only, {@code false} if not
    */publicvoidsetReadOnly(boolean readOnly){checkIfSealed();this.isReadOnly = readOnly;}/**
    * Determine whether HikariCP will self-register {@link HikariConfigMXBean} and {@link HikariPoolMXBean} instances
    * in JMX.
    *
    * @return {@code true} if HikariCP will register MXBeans, {@code false} if it will not
    */publicbooleanisRegisterMbeans(){return isRegisterMbeans;}/**
    * Configures whether HikariCP self-registers the {@link HikariConfigMXBean} and {@link HikariPoolMXBean} in JMX.
    *
    * @param register {@code true} if HikariCP should register MXBeans, {@code false} if it should not
    */publicvoidsetRegisterMbeans(boolean register){checkIfSealed();this.isRegisterMbeans = register;}/** {@inheritDoc} */@OverridepublicStringgetPoolName(){return poolName;}/**
    * Set the name of the connection pool.  This is primarily used in logging and JMX management consoles
    * to identify pools and pool configurations
    *
    * @param poolName the name of the connection pool to use
    */publicvoidsetPoolName(String poolName){checkIfSealed();this.poolName = poolName;}/**
    * Get the ScheduledExecutorService used for housekeeping.
    *
    * @return the executor
    */publicScheduledExecutorServicegetScheduledExecutor(){return scheduledExecutor;}/**
    * Set the ScheduledExecutorService used for housekeeping.
    *
    * @param executor the ScheduledExecutorService
    */publicvoidsetScheduledExecutor(ScheduledExecutorService executor){checkIfSealed();this.scheduledExecutor = executor;}publicStringgetTransactionIsolation(){return transactionIsolationName;}/**
    * Get the default schema name to be set on connections.
    *
    * @return the default schema name
    */publicStringgetSchema(){return schema;}/**
    * Set the default schema name to be set on connections.
    *
    * @param schema the name of the default schema
    */publicvoidsetSchema(String schema){checkIfSealed();this.schema = schema;}/**
    * Get the user supplied SQLExceptionOverride class name.
    *
    * @return the user supplied SQLExceptionOverride class name
    * @see SQLExceptionOverride
    */publicStringgetExceptionOverrideClassName(){returnthis.exceptionOverrideClassName;}/**
    * Set the user supplied SQLExceptionOverride class name.
    *
    * @param exceptionOverrideClassName the user supplied SQLExceptionOverride class name
    * @see SQLExceptionOverride
    */publicvoidsetExceptionOverrideClassName(String exceptionOverrideClassName){checkIfSealed();var overrideClass =attemptFromContextLoader(exceptionOverrideClassName);try{if(overrideClass ==null){
            overrideClass =this.getClass().getClassLoader().loadClass(exceptionOverrideClassName);LOGGER.debug("SQLExceptionOverride class {} found in the HikariConfig class classloader {}", exceptionOverrideClassName,this.getClass().getClassLoader());}}catch(ClassNotFoundException e){LOGGER.error("Failed to load SQLExceptionOverride class {} from HikariConfig class classloader {}", exceptionOverrideClassName,this.getClass().getClassLoader());}if(overrideClass ==null){thrownewRuntimeException("Failed to load SQLExceptionOverride class "+ exceptionOverrideClassName +" in either of HikariConfig class loader or Thread context classloader");}try{
         overrideClass.getConstructor().newInstance();this.exceptionOverrideClassName = exceptionOverrideClassName;}catch(Exception e){thrownewRuntimeException("Failed to instantiate class "+ exceptionOverrideClassName, e);}}/**
    * Set the default transaction isolation level.  The specified value is the
    * constant name from the <code>Connection</code> class, eg.
    * <code>TRANSACTION_REPEATABLE_READ</code>.
    *
    * @param isolationLevel the name of the isolation level
    */publicvoidsetTransactionIsolation(String isolationLevel){checkIfSealed();this.transactionIsolationName = isolationLevel;}/**
    * Get the thread factory used to create threads.
    *
    * @return the thread factory (may be null, in which case the default thread factory is used)
    */publicThreadFactorygetThreadFactory(){return threadFactory;}/**
    * Set the thread factory to be used to create threads.
    *
    * @param threadFactory the thread factory (setting to null causes the default thread factory to be used)
    */publicvoidsetThreadFactory(ThreadFactory threadFactory){checkIfSealed();this.threadFactory = threadFactory;}voidseal(){this.sealed=true;}/**
    * Copies the state of {@code this} into {@code other}.
    *
    * @param other Other {@link HikariConfig} to copy the state to.
    */publicvoidcopyStateTo(HikariConfig other){for(var field :HikariConfig.class.getDeclaredFields()){if(!Modifier.isFinal(field.getModifiers())){
            field.setAccessible(true);try{
               field.set(other, field.get(this));}catch(Exception e){thrownewRuntimeException("Failed to copy HikariConfig state: "+ e.getMessage(), e);}}}

      other.sealed=false;}// ***********************************************************************//                          Private methods// ***********************************************************************privateClass<?>attemptFromContextLoader(finalString driverClassName){finalvar threadContextClassLoader =Thread.currentThread().getContextClassLoader();if(threadContextClassLoader !=null){try{finalvar driverClass = threadContextClassLoader.loadClass(driverClassName);LOGGER.debug("Driver class {} found in Thread context class loader {}", driverClassName, threadContextClassLoader);return driverClass;}catch(ClassNotFoundException e){LOGGER.debug("Driver class {} not found in Thread context class loader {}, trying classloader {}",
               driverClassName, threadContextClassLoader,this.getClass().getClassLoader());}}returnnull;}@SuppressWarnings("StatementWithEmptyBody")publicvoidvalidate(){if(poolName ==null){
         poolName =generatePoolName();}elseif(isRegisterMbeans && poolName.contains(":")){thrownewIllegalArgumentException("poolName cannot contain ':' when used with JMX");}// treat empty property as null//noinspection NonAtomicOperationOnVolatileField
      catalog =getNullIfEmpty(catalog);
      connectionInitSql =getNullIfEmpty(connectionInitSql);
      connectionTestQuery =getNullIfEmpty(connectionTestQuery);
      transactionIsolationName =getNullIfEmpty(transactionIsolationName);
      dataSourceClassName =getNullIfEmpty(dataSourceClassName);
      dataSourceJndiName =getNullIfEmpty(dataSourceJndiName);
      driverClassName =getNullIfEmpty(driverClassName);
      jdbcUrl =getNullIfEmpty(jdbcUrl);// Check Data Source Optionsif(dataSource !=null){if(dataSourceClassName !=null){LOGGER.warn("{} - using dataSource and ignoring dataSourceClassName.", poolName);}}elseif(dataSourceClassName !=null){if(driverClassName !=null){LOGGER.error("{} - cannot use driverClassName and dataSourceClassName together.", poolName);// NOTE: This exception text is referenced by a Spring Boot FailureAnalyzer, it should not be// changed without first notifying the Spring Boot developers.thrownewIllegalStateException("cannot use driverClassName and dataSourceClassName together.");}elseif(jdbcUrl !=null){LOGGER.warn("{} - using dataSourceClassName and ignoring jdbcUrl.", poolName);}}elseif(jdbcUrl !=null|| dataSourceJndiName !=null){// ok}elseif(driverClassName !=null){LOGGER.error("{} - jdbcUrl is required with driverClassName.", poolName);thrownewIllegalArgumentException("jdbcUrl is required with driverClassName.");}else{LOGGER.error("{} - dataSource or dataSourceClassName or jdbcUrl is required.", poolName);thrownewIllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");}validateNumerics();if(LOGGER.isDebugEnabled()|| unitTest){logConfiguration();}}privatevoidvalidateNumerics(){if(maxLifetime !=0&& maxLifetime <SECONDS.toMillis(30)){LOGGER.warn("{} - maxLifetime is less than 30000ms, setting to default {}ms.", poolName,MAX_LIFETIME);
         maxLifetime =MAX_LIFETIME;}// keepalive time must larger then 30 secondsif(keepaliveTime !=0&& keepaliveTime <SECONDS.toMillis(30)){LOGGER.warn("{} - keepaliveTime is less than 30000ms, disabling it.", poolName);
         keepaliveTime =DEFAULT_KEEPALIVE_TIME;}// keepalive time must be less than maxLifetime (if maxLifetime is enabled)if(keepaliveTime !=0&& maxLifetime !=0&& keepaliveTime >= maxLifetime){LOGGER.warn("{} - keepaliveTime is greater than or equal to maxLifetime, disabling it.", poolName);
         keepaliveTime =DEFAULT_KEEPALIVE_TIME;}if(leakDetectionThreshold >0&&!unitTest){if(leakDetectionThreshold <SECONDS.toMillis(2)||(leakDetectionThreshold > maxLifetime && maxLifetime >0)){LOGGER.warn("{} - leakDetectionThreshold is less than 2000ms or more than maxLifetime, disabling it.", poolName);
            leakDetectionThreshold =0;}}if(connectionTimeout <SOFT_TIMEOUT_FLOOR){LOGGER.warn("{} - connectionTimeout is less than {}ms, setting to {}ms.", poolName,SOFT_TIMEOUT_FLOOR,CONNECTION_TIMEOUT);
         connectionTimeout =CONNECTION_TIMEOUT;}if(validationTimeout <SOFT_TIMEOUT_FLOOR){LOGGER.warn("{} - validationTimeout is less than {}ms, setting to {}ms.", poolName,SOFT_TIMEOUT_FLOOR,VALIDATION_TIMEOUT);
         validationTimeout =VALIDATION_TIMEOUT;}if(maxPoolSize <1){
         maxPoolSize =DEFAULT_POOL_SIZE;}if(minIdle <0|| minIdle > maxPoolSize){
         minIdle = maxPoolSize;}if(idleTimeout +SECONDS.toMillis(1)> maxLifetime && maxLifetime >0&& minIdle < maxPoolSize){LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
         idleTimeout =0;}elseif(idleTimeout !=0&& idleTimeout <SECONDS.toMillis(10)&& minIdle < maxPoolSize){LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName,IDLE_TIMEOUT);
         idleTimeout =IDLE_TIMEOUT;}elseif(idleTimeout !=IDLE_TIMEOUT&& idleTimeout !=0&& minIdle == maxPoolSize){LOGGER.warn("{} - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool.", poolName);}}privatevoidcheckIfSealed(){if(sealed)thrownewIllegalStateException("The configuration of the pool is sealed once started. Use HikariConfigMXBean for runtime changes.");}privatevoidlogConfiguration(){LOGGER.debug("{} - configuration:", poolName);finalvar propertyNames =newTreeSet<>(PropertyElf.getPropertyNames(HikariConfig.class));for(var prop : propertyNames){try{var value =PropertyElf.getProperty(prop,this);if("dataSourceProperties".equals(prop)){var dsProps =PropertyElf.copyProperties(dataSourceProperties);
               dsProps.setProperty("password","<masked>");
               value = dsProps;}if("initializationFailTimeout".equals(prop)&& initializationFailTimeout ==Long.MAX_VALUE){
               value ="infinite";}elseif("transactionIsolation".equals(prop)&& transactionIsolationName ==null){
               value ="default";}elseif(prop.matches("scheduledExecutorService|threadFactory")&& value ==null){
               value ="internal";}elseif(prop.contains("jdbcUrl")&& value instanceofString){
               value =((String)value).replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*","$1<masked>");}elseif(prop.contains("password")){
               value ="<masked>";}elseif(value instanceofString){
               value ="\""+ value +"\"";// quote to see lead/trailing spaces is any}elseif(value ==null){
               value ="none";}LOGGER.debug("{}{}",(prop +"................................................").substring(0,32), value);}catch(Exception e){// continue}}}privatevoidloadProperties(String propertyFileName){finalvar propFile =newFile(propertyFileName);try(finalvar is = propFile.isFile()?newFileInputStream(propFile):this.getClass().getResourceAsStream(propertyFileName)){if(is !=null){var props =newProperties();
            props.load(is);PropertyElf.setTargetFromProperties(this, props);}else{thrownewIllegalArgumentException("Cannot find property file: "+ propertyFileName);}}catch(IOException io){thrownewRuntimeException("Failed to read property file", io);}}privateStringgeneratePoolName(){finalvar prefix ="HikariPool-";try{// Pool number is global to the VM to avoid overlapping pool numbers in classloader scoped environmentssynchronized(System.getProperties()){finalvar next =String.valueOf(Integer.getInteger("com.zaxxer.hikari.pool_number",0)+1);System.setProperty("com.zaxxer.hikari.pool_number", next);return prefix + next;}}catch(AccessControlException e){// The SecurityManager didn't allow us to read/write system properties// so just generate a random pool number insteadfinalvar random =ThreadLocalRandom.current();finalvar buf =newStringBuilder(prefix);for(var i =0; i <4; i++){
            buf.append(ID_CHARACTERS[random.nextInt(62)]);}LOGGER.info("assigned random pool name '{}' (security manager prevented access to system properties)", buf);return buf.toString();}}privateObjectgetObjectOrPerformJndiLookup(Object object){if(object instanceofString){try{var initCtx =newInitialContext();return initCtx.lookup((String) object);}catch(NamingException e){thrownewIllegalArgumentException(e);}}return object;}}

from this code, the maxLifeTime has been updated to 30000ms at least in this version.

So now please update your HikariCP version to the latest version 2.7.4 if you want to set maxLifeTime to 30000ms.

But if you update your HikariCP version to 2.7.4 with JDK 8, i also recommend you two points:

  1. to set maxLifeTime value to be at least 30000ms.
  2. to set maxLifeTime value few minute less than mysql’s wait_timeout(show variables like “%timeout%”) to avoid broken connection exception.

if you want to get more detail. please refer to my answer in the stackoverflow:

https://stackoverflow.com/questions/28180562/hikaricp-and-maxlifetime/47694402#47694402

如果有问题可以私信我

标签: java 服务器 hikaricp

本文转载自: https://blog.csdn.net/superdangbo/article/details/128204444
版权归原作者 Python&Basketball 所有, 如有侵权,请联系我们删除。

“HikariCP实战 | 通过查看源码分析如何解决maxLifeTime配置问题”的评论:

还没有评论