?最近项目上线,但是大下午的客服反应项目登录页面很慢,SA发现后立马重启后项目访问速度恢复。 ?排查问题时,发现nginx的页面访问速度都是在十秒左右,有部分二十秒,这个时间很奇怪。日志显示,项目并没有什么特殊的错误异常。跟据经验立马确认了下连接池的maxwait时间为10000,与是百分之九十肯定是项目中有连接池未关闭的操作。
?? ?但是此项目上线已经几天,说明此未关闭的地方很隐蔽,另外项目比较大,大家一起查了下DAO的代码,也没有什么头绪,因为正常所有的DAO操作在finally都有相关close操作,对于一些需要保持连接的操作,通过keepalive,也进行了急时关闭,所以要具体问题在哪很麻烦。
?? ?另外重启后,通过show processlist,发现此应用的连接数每天都会有一些增加,虽然不是很明显,但是每天有些增加,更坚定了我的判断。
?? 由于项目是在原来老有项目中基础上开发,连接池使用了原有的tomcat自带的dbcp,其配制是通过JNDI配制。记得连接池是有自动释放连接的配制,通过google,找到资料如下:
???设置数据源的removeAbandoned="true",class="KSFIND_CLASS_SELECT">removeAbandonedTimeout="60",logAbandoned="true"几个属性就可以了。?DBCP会自动把超过timeout时间仍未关闭的连接强制关闭,并且打出异常信息(包含打开连接的代码位置) ,其中第一参数,打开自动释放未被使用的连接,而第二参数为多长时间未被使用的连接,第三个参数打印dbcp自动关闭时,输出的log4j日志。(这些参数有些地方需要说明:1.removeAbandonedTimeout一定需要估量自己项目中某个操作最长使用的时间,其中此参数为秒。2.对于数据库连接池还是我们自己控制关闭更好些,而使用上面配制是为了帮忙找出未关闭的地方在什么地方。修正后,可以关闭这些配制。3.log4j的日志配制级别一定需要在debug以上日志才能输出)
?? ?修改以上配制后,服务器做了一次更新。前四天很正常,都没有错误的日志。在我以为自己判断失误的情况下,今天早上来一到公司,习惯性的扫描起日志来,突然发现了一堆
org.apache.tomcat.dbcp.dbcp.AbandonedTrace$AbandonedObjectException: DBCP object created 2011-07-29?08:30:25 by the following code was never closed: 让我异常兴奋。然后定位到详细的日志,定位到了代码。
?? ?查看代码后,这个BUG让我们很无语。一个同事把一些本是service判断属性是否为null的逻辑代码,放到了DAO里面,而此判断又是在DAO里面的try{}finally的外面,而DB的构造是在此DAO的外层,所以构造DB实例时,当出现此属性为NULL的情况时,会直接返回。这样跟据没有执行try{}finally里面的代码,即不存在close了。而此操作又是一个小权限的用户下,且此权限下某个特定逻辑时才会触发。这也是为什么几天都没有触发的原因。
?? 到此,问题得到解决,把逻辑代码提出到外层service后,问题就得到解决了。通过此次问题的解决,让我考虑的东西又多了些。代码的review,service与dao的严格区分,DAO连接关闭的处理等等
?? ?问题解决了很开心,下面附一下默认DBCP连接池的配制参数的说明:
???maxActive :连接池中可同时连接的最大的连接数(默认值为8,调整为20,高峰单机器在20并发左右,自己根据应用场景定)
?? maxIdle:连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)
?? ?minIdle:连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,调整为5,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于?minidle个数的连接,类似于jvm参数中的Xmn设置)
?? ?maxWait ?:最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限,调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
?? ?poolPreparedStatements:开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)
?? ?maxOpenPreparedStatements:开启池的prepared 后的同时最大连接数(默认无限制,同上,未配置)
?? ?minEvictableIdleTimeMillis ?:连接池中连接,在时间段内一直空闲, 被逐出连接池的时(默认为30分钟,可以适当做调整,需要和后端服务端的策略配置相关)
?? ?removeAbandonedTimeout??:超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180)
?? ?removeAbandoned ?:超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为false,调整为true)