防止防火墙导致的数据库空闲连接断开问题的方法,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
ERROR [com.alibaba.druid.util.JdbcUtils] - close connection error java.sql.SQLRecoverableException: IO Error: Broken pipe at oracle.jdbc.driver.T4CConnection.logoff(T4CConnection.java:556) at oracle.jdbc.driver.PhysicalConnection.close(PhysicalConnection.java:3984) at com.alibaba.druid.filter.FilterChainImpl.connection_close(FilterChainImpl.java:167) at com.alibaba.druid.filter.stat.StatFilter.connection_close(StatFilter.java:254) at com.alibaba.druid.filter.FilterChainImpl.connection_close(FilterChainImpl.java:163) at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.close(ConnectionProxyImpl.java:115) at com.alibaba.druid.util.JdbcUtils.close(JdbcUtils.java:79) at com.alibaba.druid.pool.DruidDataSource.discardConnection(DruidDataSource.java:965) at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:932) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4534) at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:661) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4530) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:884) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:876) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:92)
java.net.SocketException: Broken pipe是什么意思呢?
其实就是与数据库建立的tcp连接因为某些原因断开了,而导致了“管道破裂”。一般数据库连接池会与数据库保持长连接,在需要的时候省去建立连接的过程,直接使用,而为什么这些空闲的连接会被断开呢?被谁断开了?
后来和同事讨论的过程中得知别的项目组也发生过类似的情况,而他们和这个项目的共同之处就在于服务都是在DMZ区,外网可访问,而数据库在内网,需要通过防火墙才能访问到数据库。于是去找负责维护网络、防火墙的同事了解,原来防火墙有一个TCP超时时间,目前设置的为半小时,其意义是,对于通过防火墙的所有TCP连接,如果在半小时内没有任何活动,就会被防火墙拆除,这样就会导致连接中断。在拆除连接时,也不会向连接的两端发送任何数据来通知连接已经拆除。
这是一个临时解决方法,比如将防火墙的连接超时时间调整为8小时,这样可以尽量避免空闲连接的切断,但无法完全避免,因为无法预计连接会被空闲多久,如果你的系统不是总有人访问的话,那么连接迟早会因为空闲而被切断,导致一些不可预计的问题,而调大超时时间只是缓解而已
tcp的keepalive,其实就是用来保持tcp连接的,其原理简单说就是如果一个TCP连接在指定的时间内没有任何活动,会发送一个探测包到连接的对端,检测连接的对端是否仍然存在,如果对端一定时间内仍没有对探测的响应,会再次发送探测包,发送几次后,仍然没有响应,就认为连接已经失效,关闭本地连接。
tcp keepalive并不是默认开启的,在开发程序时可以设置tcp keepalive为true,这样tcp连接在一定时间内没有任何数据报文传输则启动探测,这个时间一般是操作系统规定,Linux系统中可以通过设置net.ipv4.tcp_keepalive_time
来修改,默认是7200秒,即2小时。当然在编程时也可以设置这个时间用于当前socket,但是Java的Socket API中好像只有设置keepalive=true,并没法设置tcp_keepalive_time
当设置了tcp keepalive之后,只要tcp探测包发送的时间小于防火墙的连接超时时间,防火墙就会检查到连接中仍然有数据传输,就不会断开这个连接。
使用JDBC创建的数据库tcp连接是没有设置keepalive的,这点可以通过Linux的netstat或ss命令在数据库客户端(即应用端)验证
使用命令netstat -ano 或 ss -ano,其中参数o都是显示timer计时器,timer计时器在连接建立状态下可以对连接保活计时
netstat命令对没有开启keepalive的tcp连接显示为:off (0.00/0/0)
ss命令对没有keepalive的tcp连接,不会显示timer计时器
Oracle提供了类似tcp keepalive的机制,也就是DCD(Dead Conneciton Detection)。在$ORACLE_HOME/network/admin/sqlnet.ora文件中增加如下一行:
sqlnet.expire_time=NNN
这里NNN为分钟数,Oracle数据库会在会话IDLE时间超过这个指定的时间时,检测这个会话的对端(即客户端)是否还有效。避免客户端由于异常退出,导致会话一直存在。
同样的如果DCD的时间比防火墙切断空闲连接的时间短,连接也可以一直保持
以上几种方法要么是利用tcp连接keepalive特性,要么是采用数据库端的空闲连接检测,我们的程序中也可以主动做这种心跳检测
Druid数据库连接池从1.0.28开始,添加了druid.keepAlive属性,默认关闭
打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
看完上述内容,你们掌握防止防火墙导致的数据库空闲连接断开问题的方法的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。