1、问题场景:
1.1、服务在启动之后的第一笔或者几笔请求,处理时间较长,对于调用方来说超时,引发技术保底。
1.2、数据库连接信息配置错误,项目启动过程中却不产生任何异常,等到实际功能用到数据库了才抛出异常。 2、问题分析: 2.1、调用发系统通过http请求调用服务,设置超时时间为1s,超时时间过短,调整为3s; 2.2、应用启动之后的初始几笔请求处理时间过长,超过1s,跟踪日志发现在init-datasource中等待时间占用过多; 2.3、经过本地测试发现,在应用启动之后,在jvisualvm中查看mbeans中的druid.pool中的读写datasource的属性,启动之后的creatCount为0。在有请求进入之后才初始化了配置文件中的init连接数,导致了线上的问题; 3、解决方案:在数据库配置文件中配置读写datasource的bean中增加init-method=init的配置即可。druid官方文档中介绍,如果想要在项目启动的时候初始化配置的数据库连接数,需要显示配置或者调用init-method。
具体实现,如下图:
你可以在bean中增加init-method=init的配置,
也可以创建 dataSource 后,显示的调用 init 方法。
附 init 方法源码:
public void init() throws SQLException { if (inited) { return; } final ReentrantLock lock = this.lock; try { lock.lockInterruptibly(); } catch (InterruptedException e) { throw new SQLException("interrupt", e); } boolean init = false; try { if (inited) { return; } initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); this.id = DruidDriver.createDataSourceId(); if (this.id > 1) { long delta = (this.id - 1) * 100000; this.connectionIdSeed.addAndGet(delta); this.statementIdSeed.addAndGet(delta); this.resultSetIdSeed.addAndGet(delta); this.transactionIdSeed.addAndGet(delta); } if (this.jdbcUrl != null) { this.jdbcUrl = this.jdbcUrl.trim(); initFromWrapDriverUrl(); } for (Filter filter : filters) { filter.init(this); } if (this.dbType == null || this.dbType.length() == 0) { this.dbType = JdbcUtils.getDbType(jdbcUrl, null); } if (JdbcConstants.MYSQL.equals(this.dbType) || // JdbcConstants.MARIADB.equals(this.dbType)) { boolean cacheServerConfigurationSet = false; if (this.connectProperties.containsKey("cacheServerConfiguration")) { cacheServerConfigurationSet = true; } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) { cacheServerConfigurationSet = true; } if (cacheServerConfigurationSet) { this.connectProperties.put("cacheServerConfiguration", "true"); } } if (maxActive <= 0) { throw new IllegalArgumentException("illegal maxActive " + maxActive); } if (maxActive < minIdle) { throw new IllegalArgumentException("illegal maxActive " + maxActive); } if (getInitialSize() > maxActive) { throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActieve " + maxActive); } if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) { throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true"); } if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) { throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis"); } if (this.driverClass != null) { this.driverClass = driverClass.trim(); } initFromSPIServiceLoader(); if (this.driver == null) { if (this.driverClass == null || this.driverClass.isEmpty()) { this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); } if (MockDriver.class.getName().equals(driverClass)) { driver = MockDriver.instance; } else { driver = JdbcUtils.createDriver(driverClassLoader, driverClass); } } else { if (this.driverClass == null) { this.driverClass = driver.getClass().getName(); } } initCheck(); initExceptionSorter(); initValidConnectionChecker(); validationQueryCheck(); if (isUseGlobalDataSourceStat()) { dataSourceStat = JdbcDataSourceStat.getGlobal(); if (dataSourceStat == null) { dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType); JdbcDataSourceStat.setGlobal(dataSourceStat); } if (dataSourceStat.getDbType() == null) { dataSourceStat.setDbType(this.getDbType()); } } else { dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties); } dataSourceStat.setResetStatEnable(this.resetStatEnable); connections = new DruidConnectionHolder[maxActive]; SQLException connectError = null; try { // init connections for (int i = 0, size = getInitialSize(); i < size; ++i) { PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo); connections[poolingCount] = holder; incrementPoolingCount(); } if (poolingCount > 0) { poolingPeak = poolingCount; poolingPeakTime = System.currentTimeMillis(); } } catch (SQLException ex) { LOG.error("init datasource error, url: " + this.getUrl(), ex); connectError = ex; } createAndLogThread(); createAndStartCreatorThread(); createAndStartDestroyThread(); initedLatch.await(); init = true; initedTime = new Date(); registerMbean(); if (connectError != null && poolingCount == 0) { throw connectError; } } catch (SQLException e) { LOG.error("{dataSource-" + this.getID() + "} init error", e); throw e; } catch (InterruptedException e) { throw new SQLException(e.getMessage(), e); } finally { inited = true; lock.unlock(); if (init && LOG.isInfoEnabled()) { LOG.info("{dataSource-" + this.getID() + "} inited"); } } }
在 init 里调用 createPhysicalConnection() 创建物理连接
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException { String url = this.getUrl(); Properties connectProperties = getConnectProperties(); String user; if (getUserCallback() != null) { user = getUserCallback().getName(); } else { user = getUsername(); } String password = getPassword(); PasswordCallback passwordCallback = getPasswordCallback(); if (passwordCallback != null) { if (passwordCallback instanceof DruidPasswordCallback) { DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback; druidPasswordCallback.setUrl(url); druidPasswordCallback.setProperties(connectProperties); } char[] chars = passwordCallback.getPassword(); if (chars != null) { password = new String(chars); } } Properties physicalConnectProperties = new Properties(); if (connectProperties != null) { physicalConnectProperties.putAll(connectProperties); } if (user != null && user.length() != 0) { physicalConnectProperties.put("user", user); } if (password != null && password.length() != 0) { physicalConnectProperties.put("password", password); } Connection conn; long connectStartNanos = System.nanoTime(); long connectedNanos, initedNanos, validatedNanos; try { conn = createPhysicalConnection(url, physicalConnectProperties); connectedNanos = System.nanoTime(); if (conn == null) { throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass); } initPhysicalConnection(conn); initedNanos = System.nanoTime(); validateConnection(conn); validatedNanos = System.nanoTime(); setCreateError(null); } catch (SQLException ex) { setCreateError(ex); throw ex; } catch (RuntimeException ex) { setCreateError(ex); throw ex; } catch (Error ex) { createErrorCount.incrementAndGet(); throw ex; } finally { long nano = System.nanoTime() - connectStartNanos; createTimespan += nano; } return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos); }