手写java数据库连接池,自定义实现数据库连接池,兼容springboot
一、目標
? ? ? ? ? 用精簡的代碼實現一個類似于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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql数据类型所占空间大小
- 下一篇: jmeter生成优美的压力测试报告,jm