简体   繁体   English

如何在关机/重新部署时关闭HikariCP JNDI数据源

[英]How to Close HikariCP JNDI DataSource on Shutdown/Redeploy

I am using HikariCP 2.3.3 with Spring and Jetty 9 and am trying to resolve the fact that when I hot deploy a new war file, all of the Hikari database pool connections to MySQL are left open and idle. 我在Spring和Jetty 9中使用HikariCP 2.3.3,并试图解决以下事实:当我热部署新的war文件时,与MySQL的所有Hikari数据库池连接都保持打开和空闲状态。 I am using a JNDI lookup in my spring applicationContext file to retrieve the datasource from a Jetty context file. 我在我的spring applicationContext文件中使用JNDI查找来从Jetty上下文文件中检索数据源。

Since I cannot specify a destroy-method in the jndi-lookup like I can if I were to define a dataSource bean, I referred to this question: Should I close JNDI-obtained data source? 由于无法像在定义dataSource bean时那样在jndi-lookup中指定销毁方法,因此我提到了以下问题: 是否应关闭JNDI获得的数据源? , where it mentions you can attempt to close the datasource in the contextDestroyed() method of a ServletContextListener. ,其中提到您可以尝试在ServletContextListener的contextDestroyed()方法中关闭数据源。 In that case they were using tomcat and c3po so I'm not sure how much the example relates. 在那种情况下,他们使用的是tomcat和c3po,所以我不确定该示例涉及多少。

I have tried the following in my contextDestroyed method: 我在我的contextDestroyed方法中尝试了以下方法:

InitialContext initial;
DataSource ds;     

try
{
   initial = new InitialContext();
   ds = (DataSource) initial.lookup("jdbc/myDB");
   if (ds.getConnection() == null)
   {
       throw new RuntimeException("Failed to find the JNDI Datasource");
   }

   HikariDataSource hds = (HikariDataSource) ds;

   hds.close();
} catch (NamingException | SQLException ex)
{
   Logger.getLogger(SettingsInitializer.class.getName()).log(Level.SEVERE, null, ex);
}

But at HikariDataSource hds = (HikariDataSource) ds; 但是在HikariDataSource hds =(HikariDataSource)ds; I get the following exception: java.lang.ClassCastException: com.zaxxer.hikari.HikariDataSource cannot be cast to com.zaxxer.hikari.HikariDataSource 我收到以下异常: java.lang.ClassCastException: com.zaxxer.hikari.HikariDataSource cannot be cast to com.zaxxer.hikari.HikariDataSource

I have also tried the following after reading this issue on GitHub: Is it essential to call shutdown() on HikariDataSource? 在GitHub上阅读此问题后,我还尝试了以下方法: 在HikariDataSource上调用shutdown()是否必不可少? :

InitialContext initial;
DataSource ds;     

try 
{
     initial = new InitialContext();

     ds = (DataSource) initial.lookup("jdbc/myDB");
     ds.unwrap(HikariDataSource.class).close();

} catch (NamingException | SQLException ex) 
{
     Logger.getLogger(SettingsInitializer.class.getName()).log(Level.SEVERE, null, ex);
}

But I get the following exception: java.sql.SQLException: Wrapped connection is not an instance of class com.zaxxer.hikari.HikariDataSource at com.zaxxer.hikari.HikariDataSource.unwrap(HikariDataSource.java:177) 但是我得到以下异常: java.sql.SQLException: Wrapped connection is not an instance of class com.zaxxer.hikari.HikariDataSource at com.zaxxer.hikari.HikariDataSource.unwrap(HikariDataSource.java:177)

I feel like I'm close to a working solution but can't quite get it. 我觉得我已经接近一个可行的解决方案,但还不能完全解决。 What is the proper way to close a JNDI HikariCP data source, whether in contextDestroyed() or elsewhere? 在contextDestroyed()或其他地方,关闭JNDI HikariCP数据源的正确方法是什么?

I can't find where the 2.3.3 code lines up with the HikariDataSource.java:177 line number above. 我找不到2.3.3代码与上面的HikariDataSource.java:177行号HikariDataSource.java:177位置。 One suggestion is upgrading to the latest HikariCP version, 2.3.8. 一个建议是升级到最新的HikariCP版本2.3.8。

While your code looks correct , I suspect that you are running into a classloader issue, whereby the HikariDataSource (class) loaded by Jetty/Spring classloader and registered in JNDI is not the same classloader that is loading HikariDataSource in your web app. 当您的代码看起来正确时 ,我怀疑您遇到了类加载器问题,由Jetty / Spring类加载器加载并在JNDI中注册的HikariDataSource(类)与在Web应用程序中加载HikariDataSource的类加载器不同。

One quick way to check is to log/print both class instances like so: 一种快速的检查方法是像这样记录/打印两个类实例

...
ds = (DataSource) initial.lookup("jdbc/myDB");
logger.info("JNDI HikariDataSource : " + System.identityHashCode(ds.getClass()));
logger.info("Local HikariDataSource: " + System.identityHashCode(HikariDataSource.class));
...

If the two class objects have different hashCodes, they are not the same class. 如果两个类对象具有不同的hashCode,则它们不是同一类。 In which case you will have to investigate whether the JNDI instance is registered in the "global JNDI context". 在这种情况下,您将必须调查JNDI实例是否在“全局JNDI上下文”中注册。 If it is, that datasource can be shared across web app instances, and it is not appropriate for your web app to unilaterally shut it down. 如果是这样,则可以在Web应用程序实例之间共享该数据源,并且不适合Web应用程序单方面将其关闭。

UPDATE: Sorry I missed it. 更新:对不起,我错过了。 Re-reading your question, my guess was correct. 重新阅读您的问题,我的猜测是正确的。 Your original error: 您的原始错误:

java.lang.ClassCastException: com.zaxxer.hikari.HikariDataSource cannot be cast to
    com.zaxxer.hikari.HikariDataSource

is a clear indication that there are two classloaders that have loaded two separate instances of the HikariDataSource class. 清楚地表明,有两个类加载器已经加载了HikariDataSource类的两个单独实例。 The first is the Jetty classloader (JNDI), and the second is your web application classloader. 第一个是Jetty类加载器(JNDI),第二个是您的Web应用程序类加载器。

This indicates that the pool is shared across web applications and you probably should not try to shut it down from your application context. 这表明该池在Web应用程序之间共享,您可能不应该尝试从应用程序上下文中将其关闭。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM