close connection error java.sql.SQLRecoverableException: IO Error: Broken pipe

 java.sql.SQLRecoverableException: IO Error: Broken pipe

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天。

Author: halberd.lee

Created: 2019-09-26 Thu 21:20

Validate

相關文章
相關標籤/搜索