Raised in the pond: Connection;
1. Design and Principle
1. Basic case
As the default connection pool of the SpringBoot2 framework, HiKariCP is known as the fastest connection pool. The database connection pool and the thread pool and object pool mentioned in the previous two articles are based on the idea of pooling in terms of design, but are only implemented The methods have their own characteristics; first of all, let's look at the basic case of HiKariCP usage:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class ConPool {
private static HikariConfig buildConfig (){
HikariConfig hikariConfig = new HikariConfig() ;
// 基础配置
hikariConfig.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/junit_test?characterEncoding=utf8");
hikariConfig.setUsername("root");
hikariConfig.setPassword("123456");
// 连接池配置
hikariConfig.setPoolName("dev-hikari-pool");
hikariConfig.setMinimumIdle(4);
hikariConfig.setMaximumPoolSize(8);
hikariConfig.setIdleTimeout(600000L);
return hikariConfig ;
}
public static void main(String[] args) throws Exception {
// 构建数据源
HikariDataSource dataSource = new HikariDataSource(buildConfig()) ;
// 获取连接
Connection connection = dataSource.getConnection() ;
// 声明SQL执行
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT count(1) num FROM jt_activity") ;
// 输出执行结果
if (resultSet.next()) {
System.out.println("query-count-result:"+resultSet.getInt("num"));
}
}
}
2. Core related classes
- HikariDataSource class : collects the relevant information of the data source description, such as configuration, connection pool, connection object, state management, etc.;
- HikariConfig class : maintains the configuration management of data sources, and parameter verification, such as userName, passWord, minIdle, maxPoolSize, etc.;
- HikariPool class : Provides core capabilities for connection pool and object management in the pool, and implements query methods for pool-related monitoring data;
- ConcurrentBag class : abandons the blocking queue used in the conventional pool as a container, and customizes the concurrent container to store connection objects;
- PoolEntry class : expand the information of connection objects, such as status, time, etc., to facilitate tracking of these instantiated objects in the container;
Through the analysis of several core classes in the connection pool, we can also intuitively understand the design principle of the source code, which is similar to the object pool application summarized in the previous article, but when different components are implemented by different developers, Each has its own abstract logic.
3. Loading logic
The data source description is constructed through the configuration information, and the connection pool is instantiated based on the configuration in the construction method. In the construction of HikariPool, the ConcurrentBag container object is instantiated; the implementation details will be analyzed from the source code level below.
2. Container Analysis
1. Container structure
The container ConcurrentBag class provides PoolEntry type connection object storage, as well as basic element management capabilities, object state description; although it is held by the HikariPool object pool class, the actual operation logic is in this class;
1.1 Basic properties
The core ones are sharedList
shared collection, threadList
thread-level cache, handoffQueue
instant queue;
// 共享对象集合,存放数据库连接
private final CopyOnWriteArrayList<T> sharedList;
// 缓存线程级连接对象,会被优先使用,避免被争抢
private final ThreadLocal<List<Object>> threadList;
// 等待获取连接的线程数
private final AtomicInteger waiters;
// 标记是否关闭
private volatile boolean closed;
// 即时处理连接的队列,当有等待线程时,通过该队列将连接分配给等待线程
private final SynchronousQueue<T> handoffQueue;
1.2 Status Description
The IConcurrentBagEntry internal interface in the ConcurrentBag class, implemented by the PoolEntry class, defines the state of the connection object:
- STATE_NOT_IN_USE: not used, that is, idle;
- STATE_IN_USE: in use;
- STATE_REMOVED: discarded;
- STATE_RESERVED: reserved state, intermediate state, used when trying to evict the connection object;
2. Packaging objects
The basic ability of the container is to store the connection object, and the management of the object requires a lot of extended tracking information to effectively complete the identification in various scenarios. At this time, the introduction of the packaging class is required;
// 业务真正使用的连接对象
Connection connection;
// 最近访问时间
long lastAccessed;
// 最近借出时间
long lastBorrowed;
// 状态描述
private volatile int state = 0;
// 是否驱逐
private volatile boolean evict;
// 生命周期结束时的调度任务
private volatile ScheduledFuture<?> endOfLife;
// 连接生成的Statement对象
private final FastList<Statement> openStatements;
// 池对象
private final HikariPool hikariPool;
It should be noted here that the FastList class implements the List interface and is customized for the HiKariCP component. Compared with the ArrayList class, out of the pursuit of performance, many range checks are removed during element management.
3. Object management
Based on the general usage of connection pools, let's take a look at how connection objects are managed, such as being lent, released, abandoned, etc., and the state transition process of objects under these operations;
1. Initialization
In the description of the loading logic above, it has been mentioned that when building a data source, the connection pool will be instantiated according to the configuration. When initializing, the source code is analyzed based on two core entry points: 1. How many connection objects are instantiated, 2. .Connection object transforms wrapper object;
In the construction of the connection pool, the checkFailFast
method is executed. In this method, the judgment of the minimum idle number of MinIdle is performed. If it is greater than 0, a wrapper object is created and placed in the container;
public HikariPool(final HikariConfig config) ;
private void checkFailFast() {
final PoolEntry poolEntry = createPoolEntry();
if (config.getMinimumIdle() > 0) {
connectionBag.add(poolEntry);
}
}
Two issues need to be paid attention to. The initial state of the created connection packaging object is 0, that is, it is idle; in addition, although the value of MinIdle=4
is set in the case, the judgment here is greater than 0, and it is only placed in the container in advance. a free object;
2. The borrowed object
When the connection object is obtained from the pool, the borrow
method in the container class is actually called:
public Connection HikariPool.getConnection(final long hardTimeout) throws SQLException ;
public T ConcurrentBag.borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException ;
When executing the borrow
method, the following core steps and logic are involved:
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
// 遍历本地线程缓存
final List<Object> list = threadList.get();
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { }
}
// 增加等待线程数
final int waiting = waiters.incrementAndGet();
try {
// 遍历Shared共享集合
for (T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { }
}
// 一定时间内轮询handoff队列
listener.addBagItem(waiting);
timeout = timeUnit.toNanos(timeout);
do {
final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
}
} finally {
// 减少等待线程数
waiters.decrementAndGet();
}
}
- First traverse the local thread cache in reverse, if there is an idle connection, return the object; if not, find the shared collection;
- Before traversing the Shared shared collection, the number of waiting threads will be marked plus 1, and if there is an idle connection, it will return directly;
- When there is no idle connection in the Shared shared collection, the current thread performs queue polling for a certain period of time
handoffQueue
there may be the release of resources, or the newly added resources;
Note that when traversing the collection, the retrieved objects will judge and update the status. If a free object is obtained, it will be updated to IN_USE
status, and then return;
3. Release the object
When the connection object is released from the pool, the requite
method in the container class is actually called:
void HikariPool.recycle(final PoolEntry poolEntry) ;
public void ConcurrentBag.requite(final T bagEntry) ;
When releasing the connection object, first update the object status to idle, and then judge whether there is a waiting thread. In the borrow
method, the waiting thread will enter the polling for a certain period of time, if not, put the object in the In thread local cache:
public void requite(final T bagEntry) {
// 更新状态
bagEntry.setState(STATE_NOT_IN_USE);
// 等待线程判断
for (int i = 0; waiters.get() > 0; i++) {
if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) { }
}
// 本地线程缓存
final List<Object> threadLocalList = threadList.get();
if (threadLocalList.size() < 50) {
threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
}
}
Note that the status of the connection object involved here is changed from using to NOT_IN_USE
idle; borrow
and requite
are the two core methods in the connection pool, responsible for resource creation and recycling. ;
Finally , this article does not stand on the overall design of the HiKariCP component, but only analyzes the tip of the iceberg of the connection pool. Although it is only part of the source code, it is enough to demonstrate the author's ultimate pursuit of performance, such as: local thread cache, custom container Type, FastList, etc.; there must be many supporting reasons for it to be widely adopted.
4. Reference source code
应用仓库:
https://gitee.com/cicadasmile/butte-flyer-parent
组件封装:
https://gitee.com/cicadasmile/butte-frame-parent
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。