我在我的 spring boot 应用程序中配置了两个数据库作为主数据库和从数据库。每当应用程序从 slave db 查询任何内容时,hikari 池都会自动关闭并抛出错误 as
o.h.engine.jdbc.spi.SqlExceptionHelper (137) |> SQL Error: 0, SQLState: null
[ http-nio-2100-exec-6 ] |ERROR| o.h.engine.jdbc.spi.SqlExceptionHelper (142) |> HikariDataSource HikariDataSource (REPORT_POOL) has been closed.
[ http-nio-2100-exec-6 ] |ERROR| c.n.c.exception.GlobalExceptionHandler (485) |> Critical Error:
org.springframework.orm.jpa.JpaSystemException: Unable to acquire JDBC Connection; nested exception is org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
我的数据库配置如下:
#Master Database
master.datasource.hikari.url=jdbc:postgresql://localhost:5432/master_db
master.datasource.hikari.username=*****
master.datasource.hikari.password=*****
master.datasource.hikari.minimum-idle=10
master.datasource.hikari.maximum-pool-size=30
master.datasource.hikari.connection-timeout=30000
master.datasource.hikari.leakDetection=15000
master.datasource.hikari.idle-timeout=25000
master.datasource.hikari.max-lifetime=600000
master.datasource.hikari.auto-commit=true
#Slave Database Read
report.datasource.hikari.url=jdbc:postgresql://localhost:5432/slave_db
report.datasource.hikari.username=*****
report.datasource.hikari.password=*****
report.datasource.hikari.minimum-idle=10
report.datasource.hikari.maximum-pool-size=30
report.datasource.hikari.connection-timeout=30000
report.datasource.hikari.leakDetection=15000
report.datasource.hikari.idle-timeout=20000
report.datasource.hikari.max-lifetime=600000
report.datasource.hikari.auto-commit=true
我已经配置了两个数据源
@Bean
@Primary
@Autowired
public DataSource dataSource() {
DataSourceRouting routingDataSource = new DataSourceRouting();
routingDataSource.initDatasource(masterDataSource(), readDataSource());
return routingDataSource;
}
private DataSource masterDataSource() {
HikariConfig master = new HikariConfig();
master.setPoolName("MASTER_POOL");
master.setJdbcUrl(env.getProperty(String.format("%s.url", MASTER)));
master.setUsername((env.getProperty(String.format("%s.username", MASTER))));
master.setPassword((env.getProperty(String.format("%s.password", MASTER))));
master.setMinimumIdle(Integer.valueOf(env.getProperty(String.format("%s.minimum-idle", MASTER))));
master.setMaximumPoolSize(Integer.valueOf(env.getProperty(String.format("%s.maximum-pool-size", MASTER))));
master.setConnectionTimeout(Long.valueOf(env.getProperty(String.format("%s.connection-timeout", MASTER))));
master.setIdleTimeout(Long.valueOf(env.getProperty(String.format("%s.idle-timeout", MASTER))));
master.setMaxLifetime(Long.valueOf(env.getProperty(String.format("%s.max-lifetime", MASTER))));
master.setLeakDetectionThreshold(Long.valueOf(env.getProperty(String.format("%s.leakDetection", MASTER))));
master.setAutoCommit(Boolean.valueOf(env.getProperty(String.format("%s.auto-commit", MASTER))));
masterDataSource = new HikariDataSource(master);
return masterDataSource;
}
private DataSource readDataSource() {
HikariConfig report = new HikariConfig();
report.setPoolName("REPORT_POOL");
report.setJdbcUrl(env.getProperty(String.format("%s.url", REPORT)));
report.setUsername((env.getProperty(String.format("%s.username", REPORT))));
report.setPassword((env.getProperty(String.format("%s.password", REPORT))));
report.setMinimumIdle(Integer.valueOf(env.getProperty(String.format("%s.minimum-idle", REPORT))));
report.setMaximumPoolSize(Integer.valueOf(env.getProperty(String.format("%s.maximum-pool-size", REPORT))));
report.setConnectionTimeout(Long.valueOf(env.getProperty(String.format("%s.connection-timeout", REPORT))));
report.setIdleTimeout(Long.valueOf(env.getProperty(String.format("%s.idle-timeout", REPORT))));
report.setMaxLifetime(Long.valueOf(env.getProperty(String.format("%s.max-lifetime", REPORT))));
report.setLeakDetectionThreshold(Long.valueOf(env.getProperty(String.format("%s.leakDetection", REPORT))));
report.setAutoCommit(Boolean.valueOf(env.getProperty(String.format("%s.auto-commit", REPORT))));
report.setReadOnly(Boolean.valueOf(env.getProperty(String.format("%s.read-only", REPORT))));
try (HikariDataSource readDataSource = new HikariDataSource(report)) {
readDataSource.getConnection();
return readDataSource;
} catch (Exception e) {
log.warn("\n\n******REPORT DB NOT FOUND, CONNECTED TO MASTER DB *******\n\n");
return masterDataSource;
}
}
我有一个 DataSourceInterceptor 用于指示报告 db 的请求
@Slf4j
@Component
public class DataSourceInterceptor extends HandlerInterceptorAdapter {
protected static final String[] PREFIX_REPORT_DS = new String[] { "/admin/report/**", "/report/**" };
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String uri = request.getRequestURI();
if (StringUtil.isStartWith(uri, StringUtil.trimStricks(PREFIX_REPORT_DS))) {
log.info("Redirect to Report Database for URL: {}", uri);
DataSourceRouting.setReportRoute();
}
return true;
}
}
还有一个 DataSourceRouting 用于设置从数据库的路由
@Slf4j
public class DataSourceRouting extends AbstractRoutingDataSource {
private static final ThreadLocal<Route> routeContext = new ThreadLocal<>();
public enum Route {
MASTER, REPORT
}
public static void setReportRoute() {
routeContext.set(Route.REPORT);
}
@Override
protected Object determineCurrentLookupKey() {
return routeContext.get();
}
public void initDatasource(DataSource masterDs, DataSource reportDs) {
log.info("Datasource routing...");
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(Route.MASTER, masterDs);
dataSourceMap.put(Route.REPORT, reportDs);
this.setTargetDataSources(dataSourceMap);
this.setDefaultTargetDataSource(masterDs);
}
}
当从数据库关闭或在应用程序启动期间无法连接时,它连接到主数据库,然后应用程序没有问题但是当从数据库连接时,应用程序尝试从它查询,上面的异常被抛出