前几天有个项目部署到生成环境中之后,同事反映经常在第一次使用某些业务功能的时候,要等待好久才能加载出数据,然后再次使用该功能,就会很快能加载出数据。
刚听到这个问题,觉得会不会是数据量太大,第一次查询很慢,然后由于引入了缓存,所以后面的查询都很快,但是很快就排除了这个猜测,
因为换了查询条件,结果依然很快,说明不是缓存的问题,而且直接将sql语句拿到数据库中去查询,速度也并不慢。
感性的猜测不成立,开始从理性的角度去排查,首先查看系统日志,果然发现了报错信息:
WARN [com.atomikos.datasource.pool.ConnectionPool] - atomikos connection pool 'gaOracleDataSource': error creating proxy of connection AtomikosNonXAPooledConnection
com.atomikos.datasource.pool.CreateConnectionException: Error executing testQuery
...
Caused by: java.sql.SQLRecoverableException: IO 错误: Software caused connection abort: recv failed
...
Caused by: java.net.SocketException: Software caused connection abort: recv failed
大致意思是:执行testQuery的时候出错了,原因:软件导致了连接中断:接收失败。
连接中断?特么的什么问题?数据库挂了?没理由啊,挂了,不可能瞬间又好,因为二次访问的时候,数据加载速度又都正常了,而且也没有报错了,之后慢慢总结出,大概20分钟不访问某个数据接口,该数据接口就会出现此报错。
单从报错信息来看,这里的连接中断,应该指的的是应用服务器和数据库服务器之间的连接中断了,大家都知道,此处的连接采用的是TCP协议,TCP协议中断,有几种可能性:
1、连接的过程中,发送方和接收方有一方突然断开了连接;
2、响应方早在请求发送之前就已经断开连接,但是发送方并不知晓;
3、请求方早在响应方响应之前就已经断开连接,但是响应方并不知晓;
对于我们的项目来说,应用程序服务器很明显是连接的发起方,应用程序报接收失败,那就是说响应方没有应答,对应上面的第1和第2中情况,由于响应方是数据库服务器,经检查,该服务器的网络很畅通,也没有出现过丢包的情况,
所以判断在连接过程中,突然中断的可能性很小,那也就是说应该属于第2种情况,就是数据库服务器在应用程序发送请求之前,就单方面中断了连接。
分析到这里,连想到我们的项目是用的atomikos连接池,连接池采用的是tcp长连接,那也就是说其实在数据库服务器那方对应的接收点已经关闭了连接会话,但是连接池端并不知晓,结果当应用程序从池中获取到一个连接,尝试发起沟通的时候,就报错了。
问题来了,什么情况下数据库服务器会单方面中断连接呢?网上查了很多资料,都没说到重点,后来找到了这篇博文,说的很详细,和我的推测也基本一致,具体参考:
http://www.360doc.com/content/08/0524/18/49194_1281450.shtml
没错,防火墙会定时阻断那些闲置的TCP连接,估计设置的时间是20-30分钟左右,解决方法也很简单,在oracle的sqlnet.ora文件中增加如下代码:
SQLNET.EXPIRE_TIME=10
意思是:让oracle每10分钟,探测一下与之保持连接的客户端是否还有应答,并关闭与那些无应答的客户端的连接
OK,各位已经明白原理了吧,oracle每10分钟发送数据报探测一下客户端是否有应答,那么防火墙就不会认为这些连接是闲置的连接(因为设置的oracle探测频率小于防火墙的扫描频率),也就不会断开这些连接了。
对于连接池的testQuery机制,是可以在检测到连接失效后,自动重新创建连接,但是检测是有间隔的,而且此间隔不能很频繁,不然也会占用资源,那么如果在检测间隔内发生了中断问题,还是会存在数据加载缓慢,给用户体验带来影响的。