HikariPool源码(一)初识
1、HikariPool是什么
HikariPool是一个开源数据库连接池管理工具,以性能优秀著称。
2、从HikariPool中可以学到什么
1.HikariPool用到很多并发和线程管理工具,可以学习它们的用法。
2.有不少提升性能的用法,可以借鉴。
3.用到了一些Java的特性,使得代码看起来更友好,简洁,例如: 1)int的定义
// 以下这个定义是合法的,可用_来表示千分位,方便识别具体值是多少,而不用一个个的数
int i = 10_000;
2)通过Lambda来简化内部类的定义,例如HikariPool.java中的如下代码:
closeConnectionExecutor.execute(() -> {
quietlyCloseConnection(connection, closureReason);
if (poolState == POOL_NORMAL) {
fillPool();
}
});
不用Lambda表达式则为如下写法:
closeConnectionExecutor.execute(new Runnable() {
@Override
public void run() {
quietlyCloseConnection(connection, closureReason);
if (poolState == POOL_NORMAL) {
fillPool();
}
}
});
因为Runnable只有一个方法,因此可通过
() -> {
替换如下代码,使得代码更加简洁:
new Runnable() {
@Override
public void run() {
}
3、初识HikariPool
HikariPool的代码初看逻辑比较复杂,这里先从如何获取数据库连接开始认识它。获取连接相关的类接口如下:
3.1、HikariPool
HikariPool是连接池管理类,负责管理数据库连接。
3.2、ConcurrentBag
ConcurrentBag是一个封装的并发管理工具,负责管理池化资源,不仅仅是数据库连接,其他池化资源都可以通过它来管理。
3.2.1、类定义
// 通过泛型要求其包含的池化资源必须实现IConcurrentBagEntry接口
public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable
3.2.2、池化资源接口
// 池化资源接口
public interface IConcurrentBagEntry
{
// 池化资源的状态定义
int STATE_NOT_IN_USE = 0;
int STATE_IN_USE = 1;
int STATE_REMOVED = -1;
int STATE_RESERVED = -2;
// 通过CAS操作而非锁来提高并发效率
boolean compareAndSet(int expectState, int newState);
void setState(int newState);
int getState();
}
3.2.3、获取PoolEntry
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
ConcurrentBag代码比较多,后面将做更详细的介绍。
3.3、PoolEntry
PoolEntry是池化资源的入口类,实现了IConcurrentBagEntry接口,同时一对一持有Connection,方便对Connection管理。其创建连接代理的代码如下:
Connection createProxyConnection(final ProxyLeakTask leakTask, final long now)
{
return ProxyFactory.getProxyConnection(this, connection, openStatements, leakTask, now, isReadOnly, isAutoCommit);
}
3.4、ProxyFactory
ProxyFactory用于获取数据库连接。
3.4.1、得到连接
static ProxyConnection getProxyConnection(final PoolEntry poolEntry, final Connection connection, final FastList<Statement> openStatements, final ProxyLeakTask leakTask, final long now, final boolean isReadOnly, final boolean isAutoCommit)
{
// Body is replaced (injected) by JavassistProxyFactory
throw new IllegalStateException("You need to run the CLI build and you need target/classes in your classpath to run.");
}
1.这个方法内部抛出了异常,实际方法体会被JavassistProxyFactory替换。
3.4.2、JavassistProxyFactory.java
用于替换ProxyFactory类的方法体。
private static void modifyProxyFactory() throws NotFoundException, CannotCompileException, IOException {
System.out.println("Generating method bodies for com.zaxxer.hikari.proxy.ProxyFactory");
String packageName = ProxyConnection.class.getPackage().getName();
CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.pool.ProxyFactory");
for (CtMethod method : proxyCt.getMethods()) {
switch (method.getName()) {
// 这里对getProxyConnection方法体内容做了替换
case "getProxyConnection":
method.setBody("{return new " + packageName + ".HikariProxyConnection(?);}");
break;
case "getProxyStatement":
method.setBody("{return new " + packageName + ".HikariProxyStatement(?);}");
break;
case "getProxyPreparedStatement":
method.setBody("{return new " + packageName + ".HikariProxyPreparedStatement(?);}");
break;
case "getProxyCallableStatement":
method.setBody("{return new " + packageName + ".HikariProxyCallableStatement(?);}");
break;
case "getProxyResultSet":
method.setBody("{return new " + packageName + ".HikariProxyResultSet(?);}");
break;
case "getProxyDatabaseMetaData":
method.setBody("{return new " + packageName + ".HikariProxyDatabaseMetaData(?);}");
break;
default:
// unhandled method
break;
}
}
// 替换后的class直接放到classes目录下
proxyCt.writeFile(genDirectory + "target/classes");
}
3.5、ProxyConnection
ProxyConnection是个连接代理,持有Connection。
3.6、ProxyLeakTask
ProxyLeakTask用于监控数据库连接是否泄露,监控思路是:
1.在创建连接时通过ScheduledExecutorService延迟执行ProxyLeakTask,这个延迟执行的时间为配置的leakDetectionThreshold,如果从创建连接代理开始到超过leakDetectionThreshold还没有关闭连接代理,ProxyLeakTask就会被执行,一旦执行就说明连接代理可能泄露了。
2.如果在leakDetectionThreshold时间内连接代理被关闭,则ProxyLeakTask会被cancel,这样就不会被执行。
class ProxyLeakTask implements Runnable
{
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyLeakTask.class);
static final ProxyLeakTask NO_LEAK;
// 用于延迟调度ProxyLeakTask
private ScheduledFuture<?> scheduledFuture;
private String connectionName;
private Exception exception;
private String threadName;
private boolean isLeaked;
static
{
// 不需要监控连接泄露的ProxyLeakTask的实现类
NO_LEAK = new ProxyLeakTask() {
@Override
void schedule(ScheduledExecutorService executorService, long leakDetectionThreshold) {}
@Override
public void run() {} // 默认啥都不做
@Override
public void cancel() {} // 默认啥都不做
};
}
ProxyLeakTask(final PoolEntry poolEntry)
{
this.exception = new Exception("Apparent connection leak detected");
this.threadName = Thread.currentThread().getName();
this.connectionName = poolEntry.connection.toString();
}
private ProxyLeakTask()
{
}
void schedule(ScheduledExecutorService executorService, long leakDetectionThreshold)
{
// 通过超过leakDetectionThreshold时间后,延迟调用ProxyLeakTask,来报告泄漏信息
scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS);
}
/** {@inheritDoc} */
@Override
// 一旦被执行,说明获取连接到关闭超过了leakDetectionThreshold时间
public void run()
{
isLeaked = true;
final StackTraceElement[] stackTrace = exception.getStackTrace();
final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 5];
System.arraycopy(stackTrace, 5, trace, 0, trace.length);
exception.setStackTrace(trace);
// 下面是监控到连接泄漏的处理,这里只是记录到日志中,如果通过一个接口处理,并可以让使用者动态实现会更灵活
LOGGER.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
}
void cancel()
{
scheduledFuture.cancel(false);
if (isLeaked) { // 检查到泄漏后连接被关闭,则给一个提示信息
LOGGER.info("Previously reported leaked connection {} on thread {} was returned to the pool (unleaked)", connectionName, threadName);
}
}
}
3.7、ProxyLeakTaskFactory
ProxyLeakTaskFactory是个工厂类,用于创建ProxyLeakTask,之所以有这个工厂类,是因为有不同的ProxyLeakTask实现。可以根据配置来决定是否要监控连接泄漏。
ProxyLeakTask schedule(final PoolEntry poolEntry)
{
// 根据配置来创建不同的代理泄露监控类
return (leakDetectionThreshold == 0) ? ProxyLeakTask.NO_LEAK : scheduleNewTask(poolEntry);
}
以上,是对HikariPool的初步认识,后面将进一步做更详细的介绍。
4、HiKaripool源码地址
end.
<--感谢三连击,左边点赞和关注。
Java极客站点: javageektour.com/
转载自:https://juejin.cn/post/6844904106516037646