微信公衆號:內核小王子 以爲能夠的話歡迎關注瀏覽器
場景:公司對外網關對不少外部商戶開放,運行多年一直正常,昨天某一個客戶調用咱們接口的時候頻繁報connectiontimeout,異常以下:tomcat
該異常來自於httpclient,緣由是建立鏈接超時,也就是tcp進行三次握手的時候失敗,或者握手報文沒有到達服務端。分析可能有以下緣由:bash
通過排查後,都不是上面兩個緣由,目前現象ping包是正常的,執行如下nc命令 ,偶爾會失敗,大部分時候成功服務器
while true; do nc -w 2 -zv open.xxx.com 443 ;sleep 1;done
複製代碼
爲了排查上面問題,咱們先回顧一下這些概念,由於我發現不少人並不清楚短鏈接加上keepalive和長鏈接的區別。微信
長鏈接: tcp三次握手後,操做系統會經過發送tcp心跳包來保證鏈接不會由於空閒時間限制被回收,基於長鏈接客戶端能夠先發送一個請求,不用等服務端返回當即在發送一個請求網絡
短鏈接: tcp三次握手後,請求一次等到收到回覆後就會進行四次揮手斷開鏈接。併發
http : 目前http都是短鏈接,而且在1.1版本中採用了keep-alive機制進行重用。短鏈接加上keep-alive可讓鏈接保持活性,可是她和長鏈接的區別是長鏈接能夠不用等第一個請求的回覆收到就能夠發送第二個請求,由於tcp是一個流,而短鏈接雖然經過keep-alive保持鏈接的活性,但他必須等到第一個請求的回覆收到後,才能發送第二個請求,通常會用一個鏈接池進行復用。瀏覽器請求一個網頁,最開始是經過一個鏈接進行請求,在收到報文回覆後解析發現要加載更多的資源,例如圖片和js等資源,會在開多個鏈接,併發的請求服務端,而這些鏈接會經過keep-alive保持活性放到一個鏈接池重用,keep-alive機制是在服務端實現的,例如在tomcat的server.xml裏面配置,默認通常爲1分鐘.框架
<Connector port="8080" maxThread="50" minSpareThreads="25" maxSpareThread="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" maxKeepAliveRequest=100 keepAliveTimeout=60000/>
複製代碼
keepalive : 剛剛說到http的keepalive是在服務端實現的,而且是針對短鏈接的,有了keepalive的短鏈接咱們通常稱爲持久鏈接,而tcp的長鏈接也須要keepalive機制,客戶端和服務端會週期的發送探活報文,這個咱們能夠經過wireshake進行抓包獲取到,有了長鏈接咱們就能夠針對這個鏈接發送請求,而且能夠不用等服務端返回後在發送後續請求,爲何http沒有用長鏈接,通常請求一個網頁後用戶會花時間進行瀏覽,沒有必要用長鏈接長時間佔有資源,可是加載網頁的時候爲了加快速度,瀏覽器進行了併發,會同時建立多個鏈接進行請求,爲了防止建立多個鏈接致使服務器壓力太大,因此瀏覽器限制了同一個域名同時請求的鏈接數,因此服務端想要加快客戶端訪問速度能夠將資源放到不一樣的域名來規避瀏覽器的限制,http沒有采用長鏈接的另外一個緣由是在http1.1以及以前的版本,一個請求報文和回覆報文並不能匹配,並無定義一個序列號讓response和request對應,這樣當同時發送多個request以後,客戶端在收到response以後不知道和那個request對應,而在http2.0中有定義,因此目前的瀏覽器都是建立多個鏈接進行併發,可是單個鏈接內部必須等上一個請求回覆後才能發送下一個請求。運維
而咱們常常會在應用層也會實現一層keepalive探活,例如netty裏面IdleSateHandler,不少rpc框架例如dubbo也會單獨在應用層實現一層探活機制,有兩個緣由,一個是應用層能夠基於此作一些高可用相關的檢測,另外由操做系統發出的tcp探活包僅僅針對網絡鏈接,過於底層,不夠靈活,而且即便應用層沒有資源處理網絡請求底層仍然會對探活包進行響應,而基於應用層就更加靈活,例如服務端能夠主動管理鏈接,客戶端也能夠主動檢測服務端是否可用。tcp
好了會到正題
最終咱們發現商戶是三臺服務器一塊兒請求的,而三臺服務器應該是通過nat後是同一個ip,那麼極可能是觸發了tcp中的一個時間戳的限制,也就是若是同一個ip的請求會記錄其時間戳並進行比較,下次發送握手報文的時候,若是時間戳比上一次請求時間小,那麼會將該握手報文丟棄,若是同一個ip是同一個機器通常不會有問題,然而三臺機器相同ip可是時間戳可能不相同,若是在大批量發送請求的時候極可能會觸發該規則。咱們詢問了下運維的千夜,他確實是在兩會期間排查網絡問題的時候加了以下參數,開啓了回收機制:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
複製代碼
而系統默認
net.ipv4.tcp_timestamps = 1 表示開啓時間戳校驗
複製代碼
千夜將配置還原後,執行sysctl -p 後沒有在報超時異常。
歷史文章: