1、HikariPool是什么
HikariPool是一个开源数据库连接池管理工具,以性能优秀著称。
2、从HikariPool中可以学到什么
1.HikariPool用到很多并发和线程管理工具,可以学习它们的用法。
2.有不少提升性能的用法,可以借鉴。
3.用到了一些Java的特性,使得代码看起来更友好,简洁,例如:
1)int的定义
1 2 | // 以下这个定义是合法的,可用_来表示千分位,方便识别具体值是多少,而不用一个个的数 int i = 10_000; |
2)通过Lambda来简化内部类的定义,例如HikariPool.java中的如下代码:
1 2 3 4 5 6 | closeConnectionExecutor.execute(() -> { quietlyCloseConnection(connection, closureReason); if (poolState == POOL_NORMAL) { fillPool(); } }); |
不用Lambda表达式则为如下写法:
1 2 3 4 5 6 7 8 9 | closeConnectionExecutor.execute(new Runnable() { @Override public void run() { quietlyCloseConnection(connection, closureReason); if (poolState == POOL_NORMAL) { fillPool(); } } }); |
因为Runnable只有一个方法,因此可通过
1 | () -> { |
替换如下代码,使得代码更加简洁:
1 2 3 4 | new Runnable() { @Override public void run() { } |
3、初识HikariPool
HikariPool的代码初看逻辑比较复杂,这里先从如何获取数据库连接开始认识它。获取连接相关的类接口如下:
3.1、HikariPool
HikariPool是连接池管理类,负责管理数据库连接。
3.2、ConcurrentBag
ConcurrentBag是一个封装的并发管理工具,负责管理池化资源,不仅仅是数据库连接,其他池化资源都可以通过它来管理。
3.2.1、类定义
1 2 | // 通过泛型要求其包含的池化资源必须实现IConcurrentBagEntry接口 public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable |
3.2.2、池化资源接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 池化资源接口 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
1 | public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException |
ConcurrentBag代码比较多,后面将做更详细的介绍。
3.3、PoolEntry
PoolEntry是池化资源的入口类,实现了IConcurrentBagEntry接口,同时一对一持有Connection,方便对Connection管理。其创建连接代理的代码如下:
1 2 3 4 | 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、得到连接
1 2 3 4 5 | 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替换。
2.这里返回ProxyConnection而不是Connection会更灵活,方便实现数据库监控。 关于数据库监控参考:Java调用链跟踪关键技术(四)SQL监控
3.4.2、JavassistProxyFactory.java
用于替换ProxyFactory类的方法体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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,这样就不会被执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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实现。可以根据配置来决定是否要监控连接泄漏。
1 2 3 4 5 | ProxyLeakTask schedule(final PoolEntry poolEntry) { // 根据配置来创建不同的代理泄露监控类 return (leakDetectionThreshold == 0) ? ProxyLeakTask.NO_LEAK : scheduleNewTask(poolEntry); } |
以上,是对HikariPool的初步认识,后面将进一步做更详细的介绍。
4、HiKaripool源码地址
1.码云镜像
2.github仓库
end.
相关阅读:
Java调用链跟踪关键技术(四)SQL监控
优秀开源代码解析(一)Google guava之EventBus
Java极客站点: http://javageektour.com/