通过2周的学习梳理,对Druid的怎么操作数据库有了一个粗浅的了解。同时随着对Druid的学习,我也一直思考一个问题,怎么样设计一个连接池呢?作为一个底层框架需要考虑哪些方面呢?这篇文章是我对这个问题的思考。总体上是从战略设计、战术设计总体2个层面分析。如图所示:
战略设计
Druid的目标是什么
从Druid最早的一个tag(0.2)可以看出这个框架最早就明确要实现一些基本功能:连接池、过滤器、统计、代理、防火墙等。这几个包一直延续到了现在的版本。也就是说Druid的目标是实现一个高性能、高可用具备过滤器、监控功能的连接池。
设计原则
在明确的目标之后,那么采用什么样的设计原则呢?我猜测的是采用演进式的原则。从0.2开始到现在的1.2.8版本。Druid发展了10年的时间。
考虑的方面
作为一个好的框架应该考虑哪些方面呢?就我看来需要考虑以下几个方面:
- 性能。没有好的性能,是不会有使用者的。
- 可用性。如果数据库连接不是高可用的话。那么会严重影响框架的使用
- 扩展性。没有好的扩展性,那么要想根据公司实际情况来扩展框架就变的很困难。也会严重影响框架的使用
- 安全性。如果使用不安全。那么就不会有人再使用。
一个好的框架至少要满足以上一些条件,才有可能被广泛的应用。那么Druid又是怎么样具备这些特征的呢。
战术设计
演进原则
Druid 从0.2版本一直到现在的1.2.8。通过10年的时间来不断演进自己。表明Druid是一款很受欢迎的连接池框架。
性能
Druid的DruidDataSource在init初始化时,可以使用异步的方式。采用异步方式来创建日志、创建连接、以及清理连接。
createAndLogThread();
createAndStartCreatorThread();
createAndStartDestroyThread();
同时在这里使用锁、条件类实现创建连接的生产者/消费者模型。
可用性
- 在createAndStartDestroyThread 中shrink方法可以定期监控数据连接,对不可用、不满足条件的连接进行清除。
- 使用参数配置的方式来提高连接可用性:
- testOnBorrow
- testOnReturn
- testWhileIdle
- minEvictableIdleTimeMillis
- removeAbandoned
- removeAbandonedTimeout
扩展性
- 使用DruidPooledConnection、DruidConnectionHolder、DruidDataSource3个类把与数据库连接相关的功能进行分离。不同的类承担了部分功能。
- DruidPooledConnection主要实现了JDBC数据库连接的功能。
- DruidConnectionHolder是对一个物理连接的封装,同时加上连接池的一些参数校验
- DruidDataSource对外的使用类,包含了初始化、Filter链条调用等一些功能
- 内置了一些Filter,满足日志、统计的功能。同时使用SPI的方式支持自定义Filter。增强了对数据库的操作。
- stat统计功能实现对数据库连接的监控
安全性
当在多线程的情况下,如何保证能够安全的使用数据库连接。是连接池最基本要保证的功能。所有在Druid的源码中到处可以看到并发包中的类:
- AtomicLongFieldUpdater
- ThreadLocal
- CountDownLatch
- ReentrantLock
- Condition
总结
- Druid作为一款优秀的数据库连接池在设计的时候,一定是在战略层面上进行了规划。一个好的框架没有清晰的目标,以及架构上的规划是不会走的很远的
- 越是底层的框架,越能看到对多线程操作的示例。为我们使用Java多线程处理问题提供了宝贵的示例。
- 学习一个优秀的框架,应该从战略层面到战术层面多去思考为什么。这样等自己去设计一个系统的时候才能考虑的比较全面。