故只会在连接空闲60s后再次使用时进行检测,其实就是执行一个SQL,而在执行SQL时如果失败了,就会调用JdbcUtils.close(realConnection)关闭连接,在关闭这个连接时抛了SocketException异常,但其实这个异常倒不会对希望获取Connection执行SQL查询的程序造成太大影响,因为JdbcUtils.close()方法中捕获了这个异常,打印log,并没有上抛
(Connection x) { if (x == null) { return; } try { x.close(); } catch (Exception e) { LOG.debug("close connection error", e); } }
那么java.net.SocketException: Broken pipe是什么意思呢?
其实就是与数据库建立的tcp连接因为某些原因断开了,而导致了“管道破裂”。一般数据库连接池会与数据库保持长连接,在需要的时候省去建立连接的过程,直接使用,而为什么这些空闲的连接会被断开呢?被谁断开了?
一开始百思不得其解,想着是因为Oracle数据库主动断开了连接吗?因为某些原因,比如从服务器到数据库的连接太多?明显不是,这个项目还在试运行阶段,用的人不多,且观察Druid的连接池监控,一般建立的连接也就几个
后来和同事讨论的过程中得知别的项目组也发生过类似的情况,而他们和这个项目的共同之处就在于服务都是在DMZ区,外网可访问,而数据库在内网,需要通过防火墙才能访问到数据库。于是去找负责维护网络、防火墙的同事了解,原来防火墙有一个TCP超时时间,目前设置的为半小时,其意义是,对于通过防火墙的所有TCP连接,如果在半小时内没有任何活动,就会被防火墙拆除,这样就会导致连接中断。在拆除连接时,也不会向连接的两端发送任何数据来通知连接已经拆除。
这下数据库连接断开的原因找到了,那么这就是一个应用与数据库在不同的网络中,连接需要经过防火墙的场景中会遇到的一个典型问题,怎么能够使应用和数据库之间即使比较空闲也能够保持一定数量的长连接,是亟待解决的。
数据库会话正在执行耗时长的SQL
切断连接之前,连接对应的Oracle会话正在执行一个耗时特别长的SQL,比如存储过程而在此过程中没有任何数据输出到客户端,这样当SQL执行完成之后,向客户端返回结果时,如果TCP连接已经被防火墙中断,这时候显然会出现错误,连接中断,那么会话也就会中断。但是客户端还不知道,会一直处于等待服务器返回结果的状态。
如果客户端没有针对这种执行耗时长的SQL的连接回收机制,那么客户端这个连接将一直处于等待状态,如果客户端不断执行这种耗时长SQL,那么客户端堆积的等待连接将越来越多。
Druid连接池的removeAbandoned相关配置以及逻辑,就是为了解决这种连接回收设置的。