之前公司有个把新创建的ABS产品发送到外部媒体验证的小项目(需要在项目中记录验证状态信息,存储在数据库中,使用openjpa做持久层实现)。花了个把月的时间搞定了,产品上线运行之后(部署Websphere),爆出了一次数据库连接池不够用的问题,出现以下异常:
com.ibm.websphere.ce.j2c.ConnectionWaitTimeoutException: Connection not available, Timed out waiting for 180007
我们的连接池配置了最大10个连接,最大等待时间是默认的180s。出现这个问题让我感到很意外,系统最开始设计的时候需求里写明的是10分钟内最多7-8个请求(10分钟是因为一个请求处理需要5~10分钟,涉及到4次数据库写+n次数据库读+n次smb读+1次smb写)。在系统集成测试的时候(部署tomcat)试过同样的连接池配置同时处理30个请求都没有问题。赶紧查询了一下出问题的时候系统状态,显示只是同时有12个请求,这就让我感觉很奇怪了,为什么会出现连接池都用光了的情况。因为一个请求存在的时间比较长,数据库连接的获取和释放都做了小心处理。代码中的连接池获取和释放都是交给了Spring transaction,每次数据库读写都是单独的事务,没有理由会出现连接池不释放的情况,在测试环境中JavaSimon的监控数据也显示了数据连接池最长占用时间不过2秒多。
因为对websphere不够熟悉,只好赶紧去查看websphere的文档,发现了可疑之处,在web.xml中写resource reference的时候连接池我使用了默认的shareable模式,而在websphere的文档中写明shareable代表这在应用和连接池中间又加多了一层connection维护者,shareable资源在短请求多次使用连接的时候很有用,对我这种长请求间隔使用几次数据库连接反而引起严重副作用,连接被占住不释放,将连接池资源使用方式改成unshareable之后,问题得到解决。
* *
One key point regarding this behavior is that when the application
closes a shareable connection, the connection is not truly closed, nor
is it returned to the Free pool. Rather, it remains in the Shared
connection pool, ready for another request within the same LTC for a
connection to the same resource
**
Note:在调查过程中,有人查询openjpa的connection usage文档建议把entitymanager改成使用单个connection,显然是对entitymanager的容器管理理解不够,entitymanager不是线程安全的,spring注入的是线程安全的实例(threadlocal)。