java.sql.SQLRecoverableException: IO Error: Broken pipe
Table of Contents
1 错误信息
ERROR [com.alibaba.druid.util.JdbcUtils] - close connection error java.sql.SQLRecoverableException: IO Error: Broken pipe
2 分析
遇到这个问题,通常是程序访问服务(好比数据库)时遇到的。这之间存在着好几个网络 通讯节点:css
程序 –> 链接池 –> 网卡 –> 防火墙 –> 路由器 –> 防火墙 —> 服务端网卡 —> 服务端静态路由 –> 服务端防火墙 –> 服务监听 –> 服务html
除了程序与链接池(通常集成在一块儿),其余任何一个节点中断链接,都有可能引起这个问 题,尤为是防火墙。而通常将某个链接中断缘由,是由于这个链接空闲了太长的时间(保持 链接却不作任何事情)。网络防火墙、tcp网络、服务器本地防火墙、监听这几个节点上都 有空闲链接控制。java
下面分别经过配置链接池、服务器上的tcp网络、数据库层面、来解决空闲链接被异常中 断的问题。python
2.1 链接池
如今国内大都使用druid 做为程序的链接池。那么该链接池针对空闲链接提供了检测和 校验机制。好比在申请使用该链接时,先测试该链接是否可用,定时检查空闲链接是否 可用等,空闲链接定时执行无心义的SQL以保证会话被验证为alive,推荐配置以下:spring
spring.datasource.druid.test-while-idle=true spring.datasource.druid.validation-query=SELECT 1 FROM DUAL spring.datasource.druid.validation-query-timeout=1000 sping.datasource.druid.min-Evictable-Idle-Time-Millis=300000 sping.datasource.druid.time-Between-Eviction-Runs-Millis=3000 spring.datasource.druid.keep-alive=true spring.datasource.druid.remove-abandoned=true spring.datasource.druid.remove-abandoned-timeout=3600 spring.datasource.druid.log-abandoned=true
通常使用以上配置后,就不会再出现链接中断的问题。sql
2.2 TCP网络
-
keepalive 类Unix系统上,TCP 链接的 keepalive 能够在应用层实现,也能够在 TCP 中提供。 这个问题存在争议,所以 TCP 链接的保活探测并非 TCP 规范中的一部分。但为了方便 ,几乎全部类 Unix 系统均在 TCP 中提供了相应的功能。shell
常见类UNIX系统中的tcp keepalive:数据库
操做系统 保活定时器 AIX # no -a | grep keep tcp_keepcnt = 8 tcp_keepidle = 14400 tcp_keepintvl = 150 Linux # sysctl -A | grep keep net.ipv4.tcp_keepalive_intvl = 75 net.ipv4.tcp_keepalive_probes = 9 net.ipv4.tcp_keepalive_time = 7200 FreeBSD #sysctl -A | grep net.inet.tcp net.inet.tcp.keepidle=… net.inet.tcp.keepintvl=… 不一样系统上的各参数的时间单位不尽相同。在 AIX 上, tcp_keeidle/tcp_keepinit/tcp_keepintvl 的时间单位是 0.5 秒;而在 Linux 上, net.ipv4.tcp_keepalive_intvl 和 net.ipv4.tcp_keepalive_time 的时间单位则为秒。并 且,上述参数仅对运行在其上的服务器应用链接有效。sass
- note
- 在 Solaris 上可经过“ndd /dev/tcp \?”命令显示上述相似参数信息,而在 HP
Unix 上则可经过 nettune 或 ndd 命令进行查询。ruby
因为全部类 Unix 系统上均支持这种功能,所以,在接下来的部分中咱们将基于 AIX 系统 具体讲述上述参数的意义和做用机制。
控制参数 参数说明 tcp_keepcnt 关闭一个非活跃链接以前进行探测的最大次数,默认为 8 次 tcp_keepidle 对一个链接进行有效性探测以前运行的最大空闲时长,默认值为 14400(即 2 个小时) tcp_keepintvl 两个探测的时间间隔,默认值为 150 即 75 秒 咱们要经过设置这些参数,使其控制时间间隔要小于防火墙设置的最大空闲时间,若是不了 解防火墙的设置,能够将该tcp_keepintvl的值设置为3分钟之内,通常网络防火墙对于 空闲会话的限制不会短于这个时间。
-
tcp retries 这里有另一个现象,当链接被异常中断,可是程序这端的服务器没有收到相关终止信息 时,由原来存在的会话继续发送报文时,不会获得反馈,超过必定时间后,TCP会从新发 送该报文,直到超过最大容许重发次数。因此,有些时间,程序收到broken pipe 信息 时,是在一段时间之后(常见的是15分钟)。而在测试、开发人员的眼中,就是业务从开 始执行到报错, 中间等待了好久,好比15分钟。这里涉及到Linux内核对tcp 重发报文 次数的控制: net.ipv4.tcp_retries2 ,能够经过文件/proc/sys/net/ipv4/tcp_retries2 进行临时调整。
其规则是:配置重传次数小于9的话,就是指数增加时间,若是大于9的话,就是最大超时时间.
TCP_RTO_MIN=(HZ/5)=0.2s
TCP_RTO_MAX=(120HZ)=120s
linear_backoff_thresh = ilog2(1205)=ilog2(0x258)=9
timeout:未超过linear_backoff_thresh=9的部分按TCP_RTO_MIN 2的指数倍增加,超过 的部分按TCP_RTO_MAX线性增加
好比:
sysctl_tcp_retries2=9,则timeout=1023TCP_RTO_MIN=204.6s sysctl_tcp_retries2=11时,timeout=1023TCP_RTO_MIN+2TCP_RTO_MAX=448.6s
针对这个问题,咱们能够将重传次数设置得小一些。好比设置为3.
2.3 数据库监听
数据库也会把一些长时间没有任何操做的会话给kill掉,并不会给出任何的反馈。当程序 使用长链接,再次请求这些会话时,就会遇到报错。从数据库角度来说,能够把空闲时间 设置得更长一些,可是这样是存在风险的,日积月累后,数据库中可能存在大量的空闲连 接,因为数据库通常会限制最大链接数的。若是大量的空闲链接存在,可能致使新的链接 没法创建。
- ORACLE oracle中对空闲会话的检测是在$ORACLE_HOME/network/admin/sqlnet.ora中配置
的,参数是sqlnet.expire_time ,单位是秒。好比:
sqlnet.expire_time=180
- MySQL 设置wait_timeout 指定空闲时间,单位是秒,最长不建议超过1天。