Hikari连接池在性能和配置的灵活性方面是目前最好的。Hikari创建JDBC连接池并管理其生命周期。今天我将展示我们如何使用Hikari来创建JDBC连接池,以及如何创建多个连接池并将其缓存在google guava缓存中。
在云原生开发中,你可以有一个中央微服务,负责维护与多个数据库的通信。
与多个数据库的通信,每个数据库对应一个特定的客户。这篇博文展示了我们如何在运行时创建Hikari数据源,并在数据源缓存中管理多个Hikari数据源。为此目的,使用google guava缓存。这是因为我们可以使用一个Hikari数据源与一个特定的数据库连接。这个解决方案提供了一种连接多个客户端来连接多个数据库的方法。你可以把它想象成一个中间件,为N个客户和M个数据库管理Hikari连接池(N * M)。
HikariCP的一些好特点是
This blog对HikariCP进行了深入的分析,还展示了如何使用HikariCP创建JDBC连接池。
Hikari是一个高度可配置的API。使用Hikari,我们可以配置JDBC连接,从如何创建连接池到连接保持多长时间。有许多配置是可能的。首先,我们需要创建一个Hikari配置。使用该配置,我们可以创建一个Hikari数据源。一个数据源将一个连接池包裹在其中。一旦数据源被创建,我们就可以简单地从数据源中获取一个连接。我们甚至没有意识到Hikari正在管理一个连接池,并从连接池中回馈一个开放和自由的连接对象。
首先,我们需要创建一个Hikari配置,告诉Hikari如何管理每个JDBC连接和整个JDBC连接池。请注意,每个连接池提供对一个数据库的连接。在Hikari中,我们称它为数据源。
我们可以在application.properties文件中定义这些参数,如下所示。
application.properties文件
hikari.cp.conf.autoCommit=true
hikari.cp.conf.connectionTimeout=300000
hikari.cp.conf.idleTimeout=600000
hikari.cp.conf.maxLifetime=1800000
hikari.cp.conf.minimumIdle=2
hikari.cp.conf.maximumPoolSize=12
然后我们可以在spring配置文件中初始化该配置。该配置将在运行时被应用程序使用,以使用Hikari创建一个JDBC连接池。
import java.unit.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Bean;
import org.springframework.beans.factory.annotation.Configuration;
import org.springframework.beans.factory.annotation.Profile;
@Configuration
@Profile("cloud")
public class MyAppConfiguration {
@Bean(name = "hikariDsConfiguration")
public Properties hikariDsConfiguration(
@Value("${hikari.cp.conf.autoCommit}") String autoCommit ,
@Value("${hikari.cp.conf.connectionTimeout}") String connectionTimeout ,
@Value("${hikari.cp.conf.idleTimeout}") String idleTimeout,
@Value("${hikari.cp.conf.maxLifetime}") String maxLifetime,
@Value("${hikari.cp.conf.minimumIdle}") String minimumIdle,
@Value("${hikari.cp.conf.maximumPoolSize}") String maximumPoolSize) {
Properties properties = new Properties();
properties.setProperty("autoCommit", autoCommit);
properties.setProperty("connectionTimeout", connectionTimeout);
properties.setProperty("idleTimeout", idleTimeout);
properties.setProperty("maxLifetime", maxLifetime);
properties.setProperty("minimumIdle", minimumIdle);
properties.setProperty("maximumPoolSize", maximumPoolSize);
return properties;
}
}
使用这个Hikari配置,我们可以创建一个Hikari数据源。在创建数据源的时候,HikariCP将建立配置中定义的最低数量的JDBC连接。在Hikari数据源被创建后,我们可以要求对数据源进行JDBC连接。我们将在JdbcManager类中创建数据源。
同样的,为了提供google guava缓存配置,我们可以在应用程序中定义参数值。属性
application.properties文件
datasource.cache.maximumSize=1000
datasource.cache.expireAfterWrite=10
datasource.cache.timeUnit=HOURS
在同一个spring配置文件中,我们正在初始化guava缓存。
import java.unit.concurrent.TimeUnit;
import javax.sql.Datasource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Bean;
import org.springframework.beans.factory.annotation.Configuration;
import org.springframework.beans.factory.annotation.Profile;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
@Configuration
@Profile("cloud")
public class MyAppConfiguration {
@Bean(name= "hikariDataSourceCache")
public cache<DataSourceKey, DataSource> hikariDataSourceCache (
@Value("${datasource.cache.maximumSize}") int maximumSize ,
@Value(" ${datasource.cache.expireAfterWrite}") long expireAfterWrite,
@Value(" ${datasource.cache.timeUnit}") String timeUnit) {
return CacheBuilder.newBuilder()
.maximumSize(maximumSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.valueOf( timeUnit))
.build();
}
}
由于我们想在缓存中存储许多数据源,数据源对象将是键值对的值。我们将需要定义一个键,为此我们要定义我们自己的键对象。DataSourceKey类提供了键的定义。由于我们在数据源缓存中使用这个对象作为key,我们需要覆盖equals和hashCode,这样guava缓存就可以正确地进行key比较。
import java.unit.Properties;
public class DataSourceKey {
private String clientID;
private String dbHostUrl;
private String userName;
private String password;
private Properties connProperties;
public DataSourceKey(String clientID,String dbHostUrl, String userName,
String password) {
this.clientID = clientID;
this.dbHostUrl = dbHostUrl;
this.userName = userName;
this.password = password;
this.connProperties = generateConnProperties(dbHostUrl,userName,password);
}
//add getters
public String getKey() {
return clientID + "_" + dbHostUrl + "_" + userName;
}
private Properties generateConnProperties(String dbHostUrl, String userName,
String password ) {
connectionProperties = new Properties();
connectionProperties.setProperty("jdbc-url", dbHostUrl);
connectionProperties.setProperty("user", userName );
connectionProperties.setProperty("password", password );
return connectionProperties;
}
@Override
public boolean equals(Object obj) {
if(obj == null || !(obj instanceof DatasourceKey)) { return false; }
if(obj == this) {return true};
DataSourceKey foreignObject = (DataSourceKey) obj;
if(foreignObject.getClientID().equals(this.clientID) &&
foreignObject.getDbHostUrl().equals(this.dbHostUrl) &&
foreignObject.getUserName().equals(this.userName) &&
foreignObject.getPassword().equals(this.password) {
return true;
} else {
return false;
}
}
@Override
public int hashCode() {
String hashString = this.clientID + this.dbHostUrl +
this.userName + this.password;
return hashString.hashCode();
}
}
现在让我们创建一个JdbcManager类,创建数据源缓存,Hikari数据源,并使用数据源提供JDBC连接。
import java.sql.Connection;
import java.util.Properties;
import java.util.concurrent.Callable;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.cache.Cache;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@ Service
public class JdbcManager {
@Autowired
private Cache<DataSourcekey, DataSource> hikariDataSourceCache;
@Autowired
private Properties hikariDSConfiguration;
public JdbcManager() {
}
public Connection getConnection( String clientID, String dbHostUrl, String userName,
String password) throws RuntimeException {
DataSourceKey dataSourceKey = new DataSourceKey( clientId, dbHostUrl, userName, password );
try {
DataSource dataSouce = hikariDataSourceCache.get(dataSourceKey,
createDataSource( dataSourceKey , hikariDSConfiguration));
return dataSource.getConnection();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
Callable <DataSource> createDataSource(DataSourceKey dataSourceKey,
Properties hikariDSConfiguration) {
return new Callable<DataSource>() {
@Override
public DataSource call() throws Exception {
HikariConfig config = new HikariConfig(hikariDSConfiguration);
config.setPoolName(dataSourceKey.getKey());
config.setJdbcUrl(dataSourceKey.getConnectionProperties().getProperty("jdbc-url"));
config.setUsername(dataSourceKey.getConnectionProperties().getProperty("user"));
config.setPassword(dataSourceKey.getConnectionProperties().getProperty("password"));
DataSource dataSource = new HikariDataSource(config);
return dataSource;
}
};
}
}
QueryExecuter类是一个REST控制器,它处理所有传入的请求,并使用JdbcManager服务来执行数据库的查询。这里QueryExecuter被用来获取所有雇员的详细信息。Employee类是一个普通的POJO,所以这里没有明确显示。当*/path/getEmployees* REST端点被调用时,雇员的列表被作为响应发送。Spring boot自动将雇员类转换为JSON。在这里,雇员列表将作为一个JSON数组被发送。
注意,我没有在这里指定数据库的URL、主机和端口。我把它留给你,开发者,从一个安全的地方获取这些细节。请记住,该解决方案是为多个数据库管理JDBC池,我们可以假设为客户提供的每个云应用实例都有自己的数据库。即使只有一个由不同用户共享的云应用,用户数据之间也应该有一些数据隔离。这可能是一个不同的数据库或同一数据库,不同的表或模式。clientID是一个标识符,使用它我们可以识别客户并可以获取相应的数据库细节。
import javax.ws.rs.core.Response;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestHeader;
@RestController
public class QueryExecuter {
private JdbcManager jdbcManager;
@Autowired
public QueryExecuter( JdbcManager jdbcManager) {
//as its autowired the jdbc manager will be injected to the controller by spring boot
this.jdbcManager = jdbcManager ;
}
@RequestMapping(value="/path/getEmployees", method= {RequestMethod.GET})
public Response getResult(@RequestHeader HttpHeaders headers) {
//fetch clientId from session context
//fetch host, user, pass from some secure store for the clientId
String SQL_QUERY = "select * from emp";
List<Employee> employees = null;
try( Connection jdbcConn =
jdbcManager.getConnection(<clientId>,<dbHostUrl>,<userName>,<password>);
PreparedStatement pst = jdbcConn.prepareStatement( SQL_QUERY );
ResultSet rs = pst.executeQuery();) {
employees = new ArrayList<>();
Employee employee;
while ( rs.next() ) {
employee = new Employee();
employee.setEmpNo( rs.getInt( "empno" ) );
employee.setEname( rs.getString( "ename" ) );
employee.setJob( rs.getString( "job" ) );
employee.setMgr( rs.getInt( "mgr" ) );
employee.setHiredate( rs.getDate( "hiredate" ) );
employee.setSal( rs.getInt( "sal" ) );
employee.setComm( rs.getInt( "comm" ) );
employee.setDeptno( rs.getInt( "deptno" ) );
employees.add( employee );
}
}
return Response.ok(employees).build();
}
记住你将需要一个JDBC驱动插件来与你的数据库进行通信。该插件取决于你使用的数据库。无论使用哪种类型的数据库,这段代码都可以被利用。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
内容来源于网络,如有侵权,请联系作者删除!