druid 源码解析 (8)

昨天实现了druid的monitor项目,今天正好实现一个利用数据库和前端配置好的数据库信息,而非在yml配置中的内容。

在我们昨天的demo实现一个工具类,在工具类里去管理获取的连接池。

实现动态连接池


  /**
   * 获取当前连接池信息
   * @param connectName
   * @return
   */
  public static DruidDataSource getDataSource(String connectName,String driver, String url, String username, String password) throws SQLException {

      DruidDataSource ds = null ;
      ds = getDruidDataSource(connectName, ds);
      if(ds == null){
          ds = createDataSource(connectName,driver,url,username,password);
      }
      return ds ;
  }
  
   /**
   * 从已有连接池中获取有无连接池
   * @param connectName
   * @param ds
   * @return
   */
  public static DruidDataSource getDruidDataSource(String connectName, DruidDataSource ds) {
      for (DruidDataSource datasource : DruidDataSourceStatManager.getDruidDataSourceInstances()) {
          if (connectName.equals(datasource.getName())) {
              ds = datasource;
              break;
          }
      }
      return ds;
  }
复制代码

首先是获取连接池的类,通过jdbc连接配置的属性,调用方法,如果有已经启动的连接池,则直接调用,如果没有则调用新启动连接池的方法。



  /**
   * 获取连接池
   * @param driver
   * @param url
   * @param username
   * @param password
   */
  public static DruidDataSource createDataSource(String connectName,String driver, String url, String username, String password) throws SQLException {
      DruidDataSource dataSource = new DruidDataSource();
      dataSource.setName(connectName);
      dataSource.setDriverClassName(driver);
      dataSource.setUrl(url);
      dataSource.setUsername(username);
      dataSource.setPassword(password);
      // 设置 初始化连接  最小 连接
      dataSource.setInitialSize(5);
      dataSource.setMinIdle(5);
      //设置最大等待时间
      dataSource.setMaxWait(60000);
      //最大并发连接数
      dataSource.setMaxActive(30);
      //防止过期
      dataSource.setValidationQuery("select 1 from dual");
      dataSource.setTestOnBorrow(true);
      dataSource.setTestWhileIdle(true);
      //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      dataSource.setTimeBetweenEvictionRunsMillis(60000);
      //配置空闲连接最小存活时间
      dataSource.setMinEvictableIdleTimeMillis(300000);
      //关闭自动重连机制
      dataSource.setBreakAfterAcquireFailure(true);
      dataSource.setConnectionErrorRetryAttempts(0);

     /* //超过时间限制是否回收
     dataSource.setRemoveAbandoned(true);
     //超时时间;单位为秒。180秒=3分钟
     dataSource.setRemoveAbandonedTimeout(180);
     //关闭abanded连接时输出错误日志
     dataSource.setLogAbandoned(true);*/
      dataSource.init();
      return dataSource;
  }
  
复制代码

接下来是一个根据配置启动连接池的方法,一些配置可以写到nacos中以随时配置,最后调用 DruidDataSource.init方法创建我们需要的连接池。

/**
 * 获取当前连接池信息
 * @param dsName
 * @return
 */
public static Map<String,Object> getDataSourceStat(String dsName){
    DruidDataSource dataSource = null;
    DruidDataSource ds = getDruidDataSource(dsName,dataSource) ;
    return ds!=null ? ds.getStatData() : new HashMap<String , Object>() ;
}

/**
 * 关闭数据库连接池
 * @param dsName
 */
public static void closeDataSource(String dsName){
    DruidDataSource dataSource = null;
    dataSource = getDruidDataSource(dsName, dataSource);
    if(dataSource != null){
        dataSource.close();
    }
}

/**
 * finally最终执行
 * @param dataSource
 * @param connection
 * @param preparedStatement
 * @param resultSet
 */
public static void finallyExecute(DruidDataSource dataSource, DruidPooledConnection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
    if (connection != null) {
        try {
            connection.close();
            dataSource.removeAbandoned();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    closeAll(preparedStatement, resultSet);
}

/**
 * 关闭连接
 * @param statement
 * @param resultSet
 */
public static void closeAll(PreparedStatement statement, ResultSet resultSet){
    try {
        if (resultSet != null) {
            resultSet.close();
        }
    } catch (Exception e) {
        throw new RuntimeException("结果集资源释放失败");
    }

    try {
        if (statement != null) {
            statement.close();
        }
    } catch (Exception e) {
        throw new RuntimeException("sql声明资源释放失败");
    }
}
复制代码

最后是一些关闭链接和获取连接池信息的接口,用于返回连接池的资源

在这里我们还可以用一个缓存key来控制多个用户抢占连接池的资源,控制使得同时只有一个连接池init成功,这样可以更好的节省mysql资源。 这个缓存键可以用redis来实现。 然后我们测试一下我们的这个util类, 写个简单的测试类看下返回:

public  List<User>   getDruid() {
  List<User>   userS=new ArrayList<>();
    PreparedStatement ps = null;
    ResultSet result = null;
    String sql = "select * from user ";
    try {
        DruidDataSource druidDataSource= DataSourceToolsUtils.getDataSource("dynamic","com.mysql.jdbc.Driver",
                "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
        Connection connection= druidDataSource.getConnection();
        ps = connection.prepareStatement(sql);
                    //获取查询结果集
                   result = ps.executeQuery();
                    while(result.next()){
                        User usera=new User();
                        usera.setId(result.getInt("id"));
                        usera.setAge(result.getInt("age"));
                        usera.setName(result.getString("name"));
                        userS.add(usera);

                      }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return userS;
}
复制代码

屏幕快照 2021-11-18 下午10.45.23.png

druid连接池初始化成功了

屏幕快照 2021-11-18 下午10.46.10.png

接口也返回了sql的值。 并且多次调用都没有再init新的连接池,demo初步成功。

总结:

今天实现了一个利用动态的db信息启动连接池的demo,这个demo还有很大的进步余地: db信息的校验,连接池的共享和关闭,以及整个链接资源的监控。 但是仔细想想,都实现了,不就是druid的源码了么。 哈哈,还是需要更多的去实现一些druid不包括的内容,比如对于我们自定义查询条件的一些优化,这些都会在代码中慢慢实现的。

Guess you like

Origin juejin.im/post/7031922486803431460