1. HikariDataSource
1.1 实例化
DataSourceAutoConfiguration数据源自动配置类中引入了Hikari配置类,声明如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
配置类中定义了HikariDataSource这样一个Bean,在容器实例化对象的时候调用HikariDataSource构造函数进行初始化
1.2 属性配置
HikariDataSource在声明时使用了@ConfigurationProperties(prefix = "spring.datasource.hikari")注解,会将application.yml配置文件中以spring.datasource.hikari开头的配置注入到该对象中
2.HikariPool
2.1 获取连接入口
DataSourceAutoConfiguration自动配置类中声明了HikariDataSource数据源,Mapper层在执行SQL时会调用SqlSessionFactory持有DataSource的getConnection()方法,也就是HikariDataSource的getConnection()方法
2.2 创建HikariPool对象
HikariDataSource的getConnection()方法会创建连接池对象
public Connection getConnection() throws SQLException {
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
pool = result = new HikariPool(this);
}
}
}
return result.getConnection();
}
2.1 创建DriverDataSource对象
根据配置文件中url、用户名、密码创建DriverDataSource对象
private void initializeDataSource() {
final String jdbcUrl = config.getJdbcUrl();
final String username = config.getUsername();
final String password = config.getPassword();
final String dsClassName = config.getDataSourceClassName();
final String driverClassName = config.getDriverClassName();
final String dataSourceJNDI = config.getDataSourceJNDI();
final Properties dataSourceProperties = config.getDataSourceProperties();
DataSource ds = config.getDataSource();
if (dsClassName != null && ds == null) {
ds = createInstance(dsClassName, DataSource.class);
PropertyElf.setTargetFromProperties(ds, dataSourceProperties);
}
else if (jdbcUrl != null && ds == null) {
ds = new DriverDataSource(jdbcUrl, driverClassName, dataSourceProperties, username, password);
}
this.dataSource = ds;
}
2.2 初始化连接
如果配置了初始化失败超时时间,则会创建数据库连接
private void checkFailFast() {
// 获取初始化失败超时时间
final long initializationTimeout = config.getInitializationFailTimeout();
if (initializationTimeout < 0) {
return;
}
do {
final PoolEntry poolEntry = createPoolEntry();
} while (elapsedMillis(startTime) < initializationTimeout);
if (initializationTimeout > 0) {
throwPoolInitializationException(getLastConnectionFailure());
}
}
2.2.1 DriverDataSource创建数据库连接
private Connection newConnection() throws Exception {
Connection connection = null;
try {
String username = config.getUsername();
String password = config.getPassword();
connection = (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);
if (connection == null) {
throw new SQLTransientConnectionException("DataSource returned null unexpectedly");
}
return connection;
} catch (Exception e) {}
finally {}
}
2.2.2 构建PoolEntry
根据创建的数据库连接来构建PoolEntry
PoolEntry newPoolEntry() throws Exception{
return new PoolEntry(newConnection(), this, isReadOnly, isAutoCommit);
}
2.2.3 存储数据库连接
connectionBag.add(poolEntry);
将数据库连接放入集合集合列表中
public void add(final T bagEntry){
sharedList.add(bagEntry);
}
2.3 填充连接池
2.3.1 创建调度任务
在创建HikariPool时会定义一个调度任务用来创建minimum-idle数量的数据库连接以此来填充连接池
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
2.3.2 提交创建数据连接任务
假设minimum-idle和maximum-pool-size都为10,则此处需要创建的连接数为9,也就是会提交9个任务用来创建数据库连接,创建连接逻辑与2.2章节一致
private synchronized void fillPool(){
final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections()) - addConnectionQueueReadOnlyView.size();
if (connectionsToAdd <= 0) logger.debug("{} - Fill pool skipped, pool is at sufficient level.", poolName);
for (int i = 0; i < connectionsToAdd; i++) {
addConnectionExecutor.submit((i < connectionsToAdd - 1) ? poolEntryCreator : postFillPoolEntryCreator);
}
}
3. 获取连接
3.1 借用连接
获取数据库连接先调用HikariDataSource的getConnection()方法,然后调用连接池HikariPool的getConnection(),最后从池中借用连接
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException {
// 1. 先从线程上下文中获取连接,如果存在并且处于"空闲"状态,直接返回
// 此方式可以避免锁竞争(在Mapper层多几次与数据库的交互,就可以体现出该逻辑的优势)
final List<Object> list = threadList.get();
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
@SuppressWarnings("unchecked")
final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
}
// 2. 在2.2章节部分创建的数据库连接最终都会存放到sharedList中,此处会遍历sharedList,如果连接处于"空闲"状态,直接返回
final int waiting = waiters.incrementAndGet();
try {
for (T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
// If we may have stolen another waiter's connection, request another bag add.
if (waiting > 1) {
listener.addBagItem(waiting - 1);
}
return bagEntry;
}
}
// 3. 符合创建数据库连接条件,则创建连接并放入队列中
listener.addBagItem(waiting);
timeout = timeUnit.toNanos(timeout);
do {
final long start = currentTime();
// 从队列中取出数据库连接
final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
timeout -= elapsedNanos(start);
} while (timeout > 10_000);
return null;
}
finally {
waiters.decrementAndGet();
}
}
3.2 超时处理
在connection-timeout时间内未获取到数据库连接,则会抛出SQLException异常
private SQLException createTimeoutException(long startTime) {
logPoolState("Timeout failure ");
metricsTracker.recordConnectionTimeout();
String sqlState = null;
final Throwable originalException = getLastConnectionFailure();
if (originalException instanceof SQLException) {
sqlState = ((SQLException) originalException).getSQLState();
}
final SQLException connectionException = new SQLTransientConnectionException(poolName + " - Connection is not available, request timed out after " + elapsedMillis(startTime) + "ms.", sqlState, originalException);
if (originalException instanceof SQLException) {
connectionException.setNextException((SQLException) originalException);
}
return connectionException;
}
4. 回收连接
public void requite(final T bagEntry){
// 1. 将连接状态设置为"空闲"状态
bagEntry.setState(STATE_NOT_IN_USE);
// 2. 存在排队情况,则将"空闲"连接放入队列(3.1章节借用连接的第三个场景会从队列中获取连接)
for (int i = 0; waiters.get() > 0; i++) {
if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {
return;
}
else if ((i & 0xff) == 0xff) {
parkNanos(MICROSECONDS.toNanos(10));
}
else {
Thread.yield();
}
}
// 3.将"空闲"连接放入线程上下文中(3.1章节借用连接的第一个场景会从线程上下文中获取连接)
final List<Object> threadLocalList = threadList.get();
if (threadLocalList.size() < 50) {
threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
}
}