使用展开 oracle.jdbc.driver.T4CConnection 的 HikariCP 连接泄漏
HikariCP Connection Leak using unwrapped oracle.jdbc.driver.T4CConnection
我在使用 HikariCP 和 Unwrapped Connection 时遇到资源泄漏问题。然后是代码的一点解释。
我必须使用展开的连接来访问 oracle.sql.BFILE 的 Oracle 方法。这是从 Oracle 目录流式传输二进制文件。
数据源示例:
private static DataSource unwrapDatasource;
public static synchronized DataSource getUnwrappedDataSource() {
if (unwrapDatasource == null) {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(50);
config.setLeakDetectionThreshold(120000);
config.setJdbcUrl(DATABASEURL);
config.addDataSourceProperty("user", USERNAME);
config.addDataSourceProperty("password", DBPASSWORD);
config.addDataSourceProperty("driverType", "thin");
config.setDriverClassName("oracle.jdbc.pool.OracleDataSource");
config.setMaxLifetime(300000);
config.setPoolName("UNWRAP");
unwrapDatasource = new HikariDataSource(config);
}
return unwrapDatasource;
}
public static Connection getUnwrappedConnection() {
Connection con = null;
try {
con = this.getUnwrappedDataSource().getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class);
} catch (SQLException ex) {
//logger junk ommitted for brevity
}
return con;
}
使用 HikariCP-java7-2.4.12 因为该应用程序非常旧并且 运行 在 tomcat-6 上。
这是被报告为
泄漏的连接示例
com.zaxxer.hikari.pool.ProxyLeakTask.run(poolProxyLeak.java:91) : <Connection leak detection triggered for {}, stack trace follows>
java.lang.Exception: Apparent connection leak detected
at myapp.package.obfuscated.getUnwrapConnection(DataSourceConstants.java:253)
at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:106)
at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:85)
at myapp.package.obfuscated.AA.execute(DataBaseCallingClass.java:52)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:425)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:228)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:462)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:610)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:503)
at java.lang.Thread.run(Thread.java:745)
这是连接及其使用方法:
Connection con = null;
PreparedStatement pstmt = null;
OracleResultSet rs = null;
InputStream inputStream = null;
StringBuilder stringBuilder = new StringBuilder(128);
try {
con = DataSourceConstants.getUnwrappedConnection();
pstmt = con.prepareStatement("SELECT BFILENAME('ORACLE_DIR', 'fileDownload.12345') AS BFILE from dual" ); //hardcoded for example
rs = (OracleResultSet) pstmt.executeQuery();
rs.next(); //Assumption if rs.next() blows up, catch would grab it and resources would all attempt to close in finally block
bfile = rs.getBFILE(1);
bfile.open();
inputStream = bfile.getBinaryStream();
char c;
long size = bfile.length();
int i = 0;
do {
c= (char) inputStream.read();
stringBuilder.append(c);
} while (++i < size);
} catch (Exception ex) {
//logger ommitted but not throwing here
} finally {
//cleanup resources
try { inputStream.close(); } catch (Exception ex) {}
try { bfile.close(); } catch (Exception ex) {}
try { rs.close(); } catch (Exception ex) {}
try { pstmt.close(); } catch (Exception ex) {}
try {con.close(); } catch (Exception ex){}
}
//use stringBuilder later in built output.
所以上面的代码检索了一个未包装的 T4CConnection,这样我就可以使用 OracleResultSet 和 BFILE / getBFILE()。这行得通并且我得到了我想要的结果,但是两个单独的 类 使用此结构检索 BFILE 都在泄漏,而另一种不使用 BFILE 但使用 Unwrapped Connection 来使用 OracleCallableStatement 的方法也是漏水。我已经实例化了 3 个数据源以按 type/function 拆分所有连接器,泄漏的唯一池是使用 Datasource.getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class) 作为其连接器的池。
我是不是对未包装的连接做错了什么?为什么只有这个漏水?这是一个已知的驱动程序错误吗? (我还没想出任何关于它的文章)BFILE好像不是很流行...
你在 getUnwrappedConnection()
中所做的不是你应该做的:如果你正在解包,那么你需要确保你还保留了连接池连接,因为关闭连接就是returns 就到池子里了。所以首先从池中获取连接,只在你真正需要它的时候解包,然后,当你完成后,关闭你从数据源获得的原始连接。
不要关闭未包装的连接,因为那样会关闭实际的物理连接,而这会破坏使用连接池的目的。
简而言之:
try (Connection connection = dataSource.getConnection()) {
OracleConnection unwrapped = connection.unwrap(oracle.jdbc.driver.OracleConnection.class)
...
// Do not close (or use try-with-resources) on unwrapped
}
我在使用 HikariCP 和 Unwrapped Connection 时遇到资源泄漏问题。然后是代码的一点解释。
我必须使用展开的连接来访问 oracle.sql.BFILE 的 Oracle 方法。这是从 Oracle 目录流式传输二进制文件。
数据源示例:
private static DataSource unwrapDatasource;
public static synchronized DataSource getUnwrappedDataSource() {
if (unwrapDatasource == null) {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(50);
config.setLeakDetectionThreshold(120000);
config.setJdbcUrl(DATABASEURL);
config.addDataSourceProperty("user", USERNAME);
config.addDataSourceProperty("password", DBPASSWORD);
config.addDataSourceProperty("driverType", "thin");
config.setDriverClassName("oracle.jdbc.pool.OracleDataSource");
config.setMaxLifetime(300000);
config.setPoolName("UNWRAP");
unwrapDatasource = new HikariDataSource(config);
}
return unwrapDatasource;
}
public static Connection getUnwrappedConnection() {
Connection con = null;
try {
con = this.getUnwrappedDataSource().getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class);
} catch (SQLException ex) {
//logger junk ommitted for brevity
}
return con;
}
使用 HikariCP-java7-2.4.12 因为该应用程序非常旧并且 运行 在 tomcat-6 上。 这是被报告为
泄漏的连接示例 com.zaxxer.hikari.pool.ProxyLeakTask.run(poolProxyLeak.java:91) : <Connection leak detection triggered for {}, stack trace follows>
java.lang.Exception: Apparent connection leak detected
at myapp.package.obfuscated.getUnwrapConnection(DataSourceConstants.java:253)
at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:106)
at myapp.package.obfuscated.BB.execute(DatabaseCallingClass2.java:85)
at myapp.package.obfuscated.AA.execute(DataBaseCallingClass.java:52)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:425)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:228)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:462)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:610)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:503)
at java.lang.Thread.run(Thread.java:745)
这是连接及其使用方法:
Connection con = null;
PreparedStatement pstmt = null;
OracleResultSet rs = null;
InputStream inputStream = null;
StringBuilder stringBuilder = new StringBuilder(128);
try {
con = DataSourceConstants.getUnwrappedConnection();
pstmt = con.prepareStatement("SELECT BFILENAME('ORACLE_DIR', 'fileDownload.12345') AS BFILE from dual" ); //hardcoded for example
rs = (OracleResultSet) pstmt.executeQuery();
rs.next(); //Assumption if rs.next() blows up, catch would grab it and resources would all attempt to close in finally block
bfile = rs.getBFILE(1);
bfile.open();
inputStream = bfile.getBinaryStream();
char c;
long size = bfile.length();
int i = 0;
do {
c= (char) inputStream.read();
stringBuilder.append(c);
} while (++i < size);
} catch (Exception ex) {
//logger ommitted but not throwing here
} finally {
//cleanup resources
try { inputStream.close(); } catch (Exception ex) {}
try { bfile.close(); } catch (Exception ex) {}
try { rs.close(); } catch (Exception ex) {}
try { pstmt.close(); } catch (Exception ex) {}
try {con.close(); } catch (Exception ex){}
}
//use stringBuilder later in built output.
所以上面的代码检索了一个未包装的 T4CConnection,这样我就可以使用 OracleResultSet 和 BFILE / getBFILE()。这行得通并且我得到了我想要的结果,但是两个单独的 类 使用此结构检索 BFILE 都在泄漏,而另一种不使用 BFILE 但使用 Unwrapped Connection 来使用 OracleCallableStatement 的方法也是漏水。我已经实例化了 3 个数据源以按 type/function 拆分所有连接器,泄漏的唯一池是使用 Datasource.getConnection().unwrap(oracle.jdbc.driver.OracleConnection.class) 作为其连接器的池。
我是不是对未包装的连接做错了什么?为什么只有这个漏水?这是一个已知的驱动程序错误吗? (我还没想出任何关于它的文章)BFILE好像不是很流行...
你在 getUnwrappedConnection()
中所做的不是你应该做的:如果你正在解包,那么你需要确保你还保留了连接池连接,因为关闭连接就是returns 就到池子里了。所以首先从池中获取连接,只在你真正需要它的时候解包,然后,当你完成后,关闭你从数据源获得的原始连接。
不要关闭未包装的连接,因为那样会关闭实际的物理连接,而这会破坏使用连接池的目的。
简而言之:
try (Connection connection = dataSource.getConnection()) {
OracleConnection unwrapped = connection.unwrap(oracle.jdbc.driver.OracleConnection.class)
...
// Do not close (or use try-with-resources) on unwrapped
}