昨天实现了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;
}
复制代码
druid连接池初始化成功了
接口也返回了sql的值。 并且多次调用都没有再init新的连接池,demo初步成功。
总结:
今天实现了一个利用动态的db信息启动连接池的demo,这个demo还有很大的进步余地: db信息的校验,连接池的共享和关闭,以及整个链接资源的监控。 但是仔细想想,都实现了,不就是druid的源码了么。 哈哈,还是需要更多的去实现一些druid不包括的内容,比如对于我们自定义查询条件的一些优化,这些都会在代码中慢慢实现的。