当 Context reload="true" 时,JDBC 连接池用尽了连接.在 Tomcat 中启用 [英] JDBC connection pool runs out of connections when Context reload="true" is enabled in Tomcat

查看:17
本文介绍了当 Context reload="true" 时,JDBC 连接池用尽了连接.在 Tomcat 中启用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 Eclipse Juno 中开发 Java EE Web 应用程序.我已将 Tomcat 配置为使用 JDBC 连接池 (org.apache.tomcat.jdbc.pool) 和 PostgreSQL 数据库.以下是我项目的 META-INF/context.xml 中的配置:

I am developing a Java EE web application in Eclipse Juno. I have configured Tomcat to use JDBC connection pool (org.apache.tomcat.jdbc.pool) along with PostgreSQL database. Here are the configurations in my project's META-INF/context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Configuration for the Tomcat JDBC Connection Pool -->
    <Resource name="jdbc/someDB"
        type="javax.sql.DataSource"
        auth="Container"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://localhost:5432/somedb"
        username="postgres"
        password="12345"
        maxActive="100"
        minIdle="10"
        initialSize="10"
        validationQuery="SELECT 1"
        validationInterval="30000"
        removeAbandoned="true"
        removeAbandonedTimeout="60"
        abandonWhenPercentageFull="50" />
</Context>

我的应用程序使用 Eclipse 部署到 Tomcat,并且在 Tomcat 的 context.xml 中,一个属性 reloadable 设置为true"以在检测到更改时自动重新加载 Web 应用程序:

My application is deployed to Tomcat using Eclipse, and in Tomcat's context.xml an attribute reloadable is set to "true" to automatically reload the web application if a change is detected:

我注意到,每次发生上述自动重新加载时,都会保留 10 个以上到 PostgreSQL 数据库的连接(因为在 webapp 的 context.xml initialSize="10" 中).所以在 10 次更改后会抛出 PSQLException:

I have noticed that every time the above mentioned automatic reload is happening 10 more connections to PostgreSQL db is reserved (because in webapp's context.xml initialSize="10"). So after 10 changes a PSQLException is thrown:

org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
...

如果我手动重启 Tomcat - 一切正常,只保留了 10 个连接.

If I manually restart Tomcat - everything is fine and just 10 connections are reserved.

有没有人知道解决这个问题的方法,所以有可能在 reloadable 设置为true"的情况下进行开发,并且每次重新加载上下文时都不会导致池化更多连接?

Does anybody know the way around this issue, so it could be possible to develop with reloadable set to "true" and not cause pooling more connections every time the context is reloaded?

非常感谢您的帮助.

附言Apache Tomcat 版本 7.0.32

P.S. Apache Tomcat Version 7.0.32

推荐答案

THE SOLUTION (tl;dr)

为了解决这个问题,添加一个属性closeMethod(记录在此处),值为close";到 context.xml 文件中的 Resource 元素.

THE SOLUTION (tl;dr)

In order to solve this issue, add an attribute closeMethod (documented here) with the value "close" to the Resource element in the context.xml file.

这是我的/META-INF/context.xml 文件的正确内容:

Here's the correct content of my /META-INF/context.xml file:

<Context>
    <!-- Configuration for the Tomcat JDBC Connection Pool -->
    <Resource name="jdbc/someDB"
        type="javax.sql.DataSource"
        auth="Container"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://localhost:5432/somedb"
        username="postgres"
        password="12345"
        maxActive="100"
        minIdle="10"
        initialSize="10"
        validationQuery="SELECT 1"
        validationInterval="30000"
        removeAbandoned="true"
        removeAbandonedTimeout="60"
        abandonWhenPercentageFull="50"
        closeMethod="close" />
</Context>

注意属性closeMethod.我对其进行了测试,现在连接数严格按照 context.xml 文件中的定义保持不变!

Pay attention to the attribute closeMethod. I tested it and now the number of connections are kept STRICTLY as defined in the context.xml file!

注意
有一个时刻(与 JNDI 相关)需要处理.有关完整说明,请参阅更新 3.

NOTE
There is one moment (related to JNDI) that may be taken care of. See the UPDATE 3 for the complete description.

好的,感谢 Apache Tomcat 提交者 Konstantin Kolinko,我找到了上述解决方案.我将这个问题报告为 ASF Bugzilla 上的一个 Apache Tomcat 错误并事实证明这不是错误(请参阅更新 1).

OK, I found the above solution thanks to Apache Tomcat committor Konstantin Kolinko. I reported this issue as an Apache Tomcat bug on ASF Bugzilla and it turned out it's not a bug (see UPDATE 1).

=== 更新 1 (2012-12-03) 又名新希望" ===

好吧,结果证明它仍然是一个错误.Mark Thomas,Apache Tomcat 7 发布经理,确认(引用):

Well, it still turned out to be a bug. Mark Thomas, the Apache Tomcat 7 release manager, confirmed that (quote):

"这是 jdbc-pool 中的内存泄漏错误.PoolCleaner 实例是保留对 ConnectionPool 的引用防止它被GC'd.
...
这已在主干和 7.0.x 中修复,并将包含在7.0.34 以后."

"This is a memory leak bug in jdbc-pool. PoolCleaner instances are retaining references to the ConnectionPool preventing it from being GC'd.
...
This has been fixed in trunk and 7.0.x and will be included in 7.0.34 onwards."

所以如果你有一个较旧的 Tomcat 版本(低于 7.0.34),请使用上面的解决方案,否则,从 Apache Tomcat 版本 7.0.34 开始,应该没有我描述的问题.(见更新 2)

So if you have an older Tomcat version (less than 7.0.34), use the above solution, otherwise, starting with Apache Tomcat version 7.0.34, there should be no issues like the one I described. (see UPDATE 2)

=== 更新 2 (2014-01-13) 又名问题反击" ===

我的错误报告中最初描述的问题似乎是即使对于当前最新的 Apache Tomcat 版本 7.0.50 仍然存在,我也用 Tomcat 7.0.47 复制了它(感谢 Miklos Krivan 指出).虽然现在Tomcat有时会在重载后设法关闭额外的连接,有时重载后连接数会增加然后保持稳定,但最终这种行为仍然不可靠.

It seems like the issue initially described in my bug report is still present even for the currently latest Apache Tomcat version 7.0.50 and I also reproduced it with Tomcat 7.0.47 (thanks to Miklos Krivan for pointing it out). Although now Tomcat sometimes manages to close additional connections after reloading, and sometimes the number of connections are increased after one reload and then kept steady, but eventually this behavior is still not reliable.

我仍然可以重现最初描述的问题(虽然同样不是那么容易:它可能与连续重新加载的频率有关).似乎这只是时间问题,即如果 Tomcat 在重新加载后有足够的时间,它会或多或少地管理连接池.正如马克·托马斯在他的评论(引用)中提到的:"根据 closeMethod 的文档,该方法的存在仅是为了加速资源的释放,否则这些资源将被 GC 释放."(引用结束),看起来速度是决定因素.

I still could reproduce the initially described issue (although again not that easy: it may be related to the frequency of successive reloads). Seems like it's just a matter of time, i.e. if Tomcat has enough time after reload, it manages the connection pool more or less as it should. As Mark Thomas mentioned in his comment (quote): "As per the docs for closeMethod, that method exists solely to speed up the freeing of resources that would otherwise be freed by GC." (end of quote), and it looks like the speed is the defining factor.

当使用 Konstantin Kolinko 提供的解决方案(使用 closeMethod=close")时,一切正常,并且保留的连接数严格按照 context.xml 文件中的定义进行保留.所以看起来使用 closeMethod="close"是(目前)避免在上下文重新加载后耗尽连接的唯一真正方法.

When using the solution presented by Konstantin Kolinko (to use closeMethod="close") everything WORKS just fine, and the number of connections reserved are kept STRICTLY as defined in the context.xml file. So it appears that using closeMethod="close" is the ONLY true way (at the moment) to avoid running out of connections after context reloading.

=== UPDATE 3 (2014-01-13) 又名Tomcat 发布经理的回归" ===

解决了更新 2 中描述的行为背后的谜团.在我收到 Mark 的回复后,更多细节现在已被清除Thomas(Tomcat 发布经理).我希望这是最后一次更新.因此,正如更新 1 中提到的那样,该错误确实已修复.我将 Mark 回复中的重要部分作为引用发布(强调我的):

The mystery behind the behavior described in the UPDATE 2 is solved. More details have been cleared now after I received a reply from Mark Thomas (Tomcat release manager). I hope this is the last update. So the bug was indeed fixed as was mentioned in the UPDATE 1. I am posting the essential part from Mark's reply here as a quote (emphasis mine):

在调查这个bug时发现的实际内存泄漏已经根据评论 #4 到 #6,在 7.0.34 之后修复.

The actual memory leak found while investigating this bug has been fixed in 7.0.34 onwards as per comments #4 to #6.

重新加载时连接未关闭的问题是由于JNDI 资源的 J2EE 规范和这部分错误因此报告无效.我正在将此错误的状态恢复到已修复以反映确实存在的内存泄漏已修复.

The issue of the connections not being closed on reload is a result of the J2EE specification for JNDI resources and this part of the bug report is therefore invalid. I am restoring the state of this bug to fixed to reflect that the memory leak that did exist has been fixed.

扩展为什么之后无法立即关闭连接重新加载无效,J2EE 规范没有提供机制容器告诉资源它不再需要.因此所有容器可以做的是清除对资源的引用并等待垃圾收集(这将触发池​​的关闭和关联的连接).垃圾收集发生在确定的时间由 JVM 所以这就是为什么它需要不确定的时间在上下文重新加载后作为垃圾关闭连接收集可能在一段时间内不会发生.

To expand on why the failure to immediately close connection after reload is invalid, the J2EE specification provides no mechanism for the container to tell the resource it is no longer required. Therefore all the container can do is clear references to the resource and wait for garbage collection (which will trigger the closure of the pool and the associated connections). Garbage collection occurs at times determined by the JVM so this is why it takes an indeterminate amount of time for connections to be closed after a context reload as a garbage collection may not occur for some time.

Tomcat 添加了 Tomcat 特定的 JNDI 属性 closeMethod可用于在出现以下情况时触发 JNDI 资源的显式关闭上下文已停止.如果等待GC清理资源不是可以接受然后只需使用此参数.Tomcat 不使用这个默认情况下,它可能会产生意想不到的和不需要的副作用一些 JNDI 资源.

Tomcat has added the Tomcat specific JNDI attribute closeMethod which can be used to trigger the explicit close of a JNDI resource when a context is stopped. If waiting for GC to clean up resources is not acceptable then simply use this parameter. Tomcat does not use this by default as it may have unexpected and unwanted side-effects for some JNDI resources.

如果你想看到一个标准的机制来告诉 JNDI不再需要它们的资源,那么你需要游说J2EE专家组.

If you'd like to see a standard mechanism provided for telling JNDI resources that they are no longer required then you need to lobby the J2EE expert group.

结论

只需使用本文开头介绍的解决方案(但为了以防万一,请记住理论上使用它可能会出现的 JNDI 相关问题).

Conclusion

Just use the solution presented in the beginning of this post (but, just in case, keep in mind the JNDI related issue that can theoretically arise from using it).

Michael Osipov 建议使用他的 CloseableResourceListener,它可以防止在取消部署 Web 应用程序期间由于开放资源导致内存泄漏.所以你也可以尝试一下.

Michael Osipov suggested using his CloseableResourceListener, which prevents memory leaks caused by left open resources during undeployment of web applications. So you may also give it a try.

免责声明
更新的别名的灵感来自星球大战电影系列.所有权利均属于其各自所有者.

DISCLAIMER
The aliases for the UPDATES were inspired by the Star Wars film series. All rights belong to their respective owners.

这篇关于当 Context reload="true" 时,JDBC 连接池用尽了连接.在 Tomcat 中启用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆