一、目标

用精简的代码实现一个类似于Hikari,Druid一样的高性能数据库连接池。

二、实现思路

1:新建连接池配置类保存连接池配置。

2:实现DataSource接口。

3:新增SmpDbPool类,内部维护一个阻塞队列保存数据库连接,并提供数据库连接的获取回收等方法。

4:大致类图:

三、核心代码

1:数据库连接代理类SmpConnectionProxy

SmpConnectionProxy实现jdbc的Connection接口

重写close方法,确保外部调用close的时候将连接归还到连接池。

public void close() throws SQLException {if (this.closed) return;this.closed = true;//回滚掉未提交的脏statementif (this.isCommitStateDirty && !this.isAutoCommit){this.delegate.rollback();//}this.clearWarnings();this.smpDbPool.releaseConnection(this);}

新增判断连接是否有效方法供连接池类使用。

public boolean isValid() throws SQLException {if (System.currentTimeMillis() - this.lastActiveTime < this.smpDbPool.getConfig().getKeepaliveTimeInMill()) return true;long validationTimeout = this.smpDbPool.getConfig().getValidationTimeoutInMill();this.delegate.setNetworkTimeout(this.smpDbPool.getNetTimeoutExecutor(), (int) validationTimeout);int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;return this.delegate.isValid(validationSeconds);}

2:数据源类SmpDataSource

SmpDataSource实现DataSource接口

实现getConnection方法,以便外界从连接池获取连接。

@Overridepublic Connection getConnection() throws SQLException {try {return this.dbPool.borrowConnection();} catch (Exception e) {LOG.error("getConnection error", e);throw new SQLException(e);}}

3:连接池类SmpDbPool

获取数据库连接方法:

public Connection borrowConnection() throws Exception {boolean isDebugEnable = LOG.isDebugEnabled();long sTime = 0;if (isDebugEnable){//尽量少访问临界资源sTime = System.currentTimeMillis();}try {SmpConnectionProxy connection = this.getConnectionFromQueue();if (connection != null) return connection;//未获取到有效连接,校验是否创建连接int maxActive = this.smpConfig.getMaxActive(), dbAmount = createdDbConnectionAmount.get();if (dbAmount >= maxActive){//尝试等待其他线程释放连接connection = this.dbConnectQueue.poll(this.smpConfig.getMaxWaitInMill(), TimeUnit.MILLISECONDS);if (connection != null) return connection.borrowConnection();LOG.error("get connection error, no connection available,maxActive:{}, activated connection:{}", maxActive, dbAmount);throw new IllegalStateException("database connection pool too busy");}connection = this.createConnectionForPool();if (connection != null) return connection;throw new IllegalStateException("database connection pool too busy");} finally {if (isDebugEnable){LOG.debug("get database connection cost {} ms", System.currentTimeMillis() - sTime);}}}

从阻塞队列获取数据库连接方法:

private SmpConnectionProxy getConnectionFromQueue() throws SQLException {int times = 0;while (times++ < 1000){SmpConnectionProxy connection = this.dbConnectQueue.poll();if (connection == null) return null;if (connection.isValid()) return connection.borrowConnection();//destroy invalid connectionconnection.destroy();createdDbConnectionAmount.decrementAndGet();LOG.info("destroyed invalid jdbc connection");}return null;}

新建数据库连接:

private SmpConnectionProxy createConnectionForPool() throws Exception{boolean locked = false;try {locked = GET_CONNECTION_LOCK.tryLock(this.smpConfig.getMaxWaitInMill() >> 1, TimeUnit.MILLISECONDS);if (!locked){LOG.error("get lock to create connection error");return null;}SmpConnectionProxy connection = this.getConnectionFromQueue();if (connection != null) return connection;int dbAmount = createdDbConnectionAmount.get(), maxActive = this.smpConfig.getMaxActive();if (dbAmount >= maxActive){LOG.error("get connection error, no connection available,maxActive:{}, activated connection:{}", maxActive, dbAmount);return null;}connection = this.createConnection();createdDbConnectionAmount.addAndGet(1);return connection.borrowConnection();} catch (ClassNotFoundException ex){LOG.error("class {} not found", this.smpConfig.getDriverClassName(), ex);throw new SQLException(ex);}finally {if (locked){GET_CONNECTION_LOCK.unlock();}}}private SmpConnectionProxy createConnection() throws ClassNotFoundException, SQLException {Class.forName( this.smpConfig.getDriverClassName() );Connection connection = DriverManager.getConnection( this.smpConfig.getUrl(), this.smpConfig.getUsername(), this.smpConfig.getPassword());LOG.debug("create new database connection with url:{}", this.smpConfig.getUrl());return new SmpConnectionProxy(connection, this, connection.getAutoCommit());}

4:构建spring-boot-starter

这里需要说明的是springboot默认会尝试使用com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource作为连接池,相关代码逻辑在org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration,因此配置spring-boot-starter的时候先要确保应用没有配置连接池。

核心代码:

5:springboot使用

添加依赖后再添加如下配置:

spring.datasource.type=com.lauor.smpdb.SmpDataSource
#最小连接数,默认4
spring.datasource.smpdb.minIdle=4
#最大连接数,默认40
spring.datasource.smpdb.maxActive=20
#数据库连接有效期检查间隔时间ms,默认30分钟,最小1s
spring.datasource.smpdb.keepaliveTimeInMill=1800000
#获取数据库连接最大等待时间ms,默认30s,最小1s
spring.datasource.smpdb.maxWaitInMill=30000
#数据连接合法性校验超时时间,默认5s,最小1s
spring.datasource.smpdb.validationTimeoutInMill=5000

连接池完整代码:https://gitee.com/tandatda/smpdb

连接池使用完整demo:https://gitee.com/tandatda/demo-edr-smpdb

手写java数据库连接池,自定义实现数据库连接池,兼容springboot相关推荐

  1. 安全系列之——手写JAVA加密、解密

    其他文章: 安全系列之--手写JAVA加密.解密 安全系列之--数据传输的完整性.私密性.源认证.不可否认性 安全系列之--主流Hash散列算法介绍和使用 安全系列之--RSA的公钥私钥有多少人能分的 ...

  2. 【XML和Java】手写Java程序引用xsd验证xml

    一.首先要有一个xml文件和xsd文件 (1) database.conf.xml <?xml version="1.0" encoding="UTF-8" ...

  3. 视频教程-手写Java框架系列教程之一反射(含配套资料)-Java

    手写Java框架系列教程之一反射(含配套资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国移动等知名企业 ...

  4. 手写Java线程池_超详细解说_绝对能运行_代码超详细注释

    线程池 问题背景 只是单纯使用 new Thread(runnable).start(); 的方式创建线程, 将会导致严重的程序性能问题: 1.线程创建, 销毁需要消耗很大的系统资源; 2.虚拟机创建 ...

  5. android 手写签批_Android自定义实现手写签名功能

    一.Android自定义View步骤 : 自定义属性: 选择和设置构造方法: 重写onMeasure()方法: 重写onDraw()方法: 重写onLayout()方法: 重写其他事件的方法(滑动监听 ...

  6. java linkedlist底层_手写Java LinkedList核心源码

    上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...

  7. android 手写签批_Android自定义View——手写签批

    接到一个领导批示保留原笔迹的功能,类似于绘画板,用户打开后可以绘制,点击完成后以图片的形式保存在本地,并且显示绘制后图片,上传服务器,达到保留原笔迹的目的.可以运用于签字.审批等. 效果图: 手写签批 ...

  8. 1000题!!阿里P8架构师手写“Java面试宝典”带你横扫全网

    序言 很多同学学习Java并发一头扎进源码,最后头破血流,无功而返.横看成岭侧成峰,远近高低各不同.学习要始终从不同的视角来看待问题.学习并发亦是如此,需要通过理论远看轮廓,然后通过源码近看明细. 今 ...

  9. 手写java_手写java锁

    手写非公平可重入锁 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁. 优点:所有的线程都能得到资源,不会饿死在队列中. 缺点:吞吐量会下降很多,队列 ...

最新文章

  1. 一个快速实现彩屏应用的跨平台快速原型开发工具平台,最重要的是还免费!8ms.xyz平台原以为是单片机版墨刀,今天上去玩了才知道平台厉害的很,基于WEB端免搭建开发环境,跑的还是C代码编译出来的程序!
  2. 菜鸟学Linux 第034篇笔记 vmlinuz, initrd, modules, script
  3. 新版手机浏览器_夸克浏览器发布全新3.0版,AI技术创新智能化信息服务
  4. java 同步块(Java Synchronized Blocks)
  5. mongooes怎么链接mysql_如何使用Node + Mongoose连接远程MongoDB数据库
  6. 让基础设施代码化更加容易,pulumi 都做了些什么?
  7. 【优化算法】多目标水循环算法(MOWCA)【含Matlab源码 1433期】
  8. 【数学建模】2021年美赛C题思路(预测模型和图像识别)【含Matlab源码 245期】
  9. 最好用的ps/lr滤镜插件ON1 photo raw 2021mac
  10. 使用selenium爬验证码图片并识别
  11. 775针服务器cpu性能排行,e7500(775针cpu天梯图)
  12. 【python】模拟斗牛纸牌游戏「牛牛」
  13. Java的在哪里找labor_LaborDay哪里玩
  14. 如何做内网穿透,在家里连回公司服务器做操作
  15. 邮箱自动化(smtplib模块)--以邮件正文HTML表格形式
  16. 【每日一道智力题】之 药瓶毒鼠鼠
  17. ubuntu20.04安装搜狗sogou输入法
  18. 背景动态线条js特效html5代码
  19. 老师助手为什么总是服务器错误,【家长助手】孩子在校总犯错 解决内因最关键...
  20. [Java大厂必备面试题] 点滴促就辉煌, 每日三题【Day5】:基础篇2

热门文章

  1. LLBLGen 关于类型转换
  2. SharePoint工作流解决方案QuickFlow系列(2)--Task
  3. ASP.Net2.0 数据绑定控件的优越性在哪里?
  4. 怎样对流媒体进行压力测试_暖气片怎样安装效果好?暖气片正确的安装,采暖效果更好!...
  5. Linux Centos6.5如何截图
  6. arm中的.a文件如何产生的_可变文件系统:如何在IPFS中处理文件?
  7. ceph与hdfs的比较_分布式存储中HDFS与Ceph两者的区别是什么,各有什么优势?
  8. 【Python金融量化 1- 100 】了解Python及常用财经数据接口包
  9. 化工仪表和自动化(自动控制系统)
  10. 三十二、从0到1教你用Scrapy来爬取整站天气网