1. 程式人生 > >服務端大量CLOSE_WAIT問題的解決

服務端大量CLOSE_WAIT問題的解決

今天在執行伺服器的時候發現一個問題,問題的表現是客戶端一直在請求,但是返回給客戶端的資訊是異常,服務端壓根沒有收到請求,查看了一下配置資訊沒有錯誤,首先查看了一下是不是伺服器的連線已經滿了,開啟n …

今天在執行伺服器的時候發現一個問題,問題的表現是客戶端一直在請求,但是返回給客戶端的資訊是異常,服務端壓根沒有收到請求,查看了一下配置資訊沒有錯誤,首先查看了一下是不是伺服器的連線已經滿了,開啟netstat命令發現伺服器的連線有大 量的CLOSEWAIT狀態的socket,沒怎麼遇到這個問題,開始還真有段懵了,第一反應就是是不是客戶端的問題(是不是出問題的第一反應都是別人 的問題),但是馬上補充了一下socket狀態機的知識,發現這個狀態是由於客戶端關閉了socket連線,傳送了FIN報文,服務端也傳送了ACK報 文,此時客戶端處於FINWAIT2狀態,服務端處於CLOSEWAIT狀態,如下圖:

33d09c9edf4607f5fef094795d84099a92d1441f

可以看出,出現問題的原因是由於我這邊沒有傳送第二個FIN報文導致的,分明是我的問題啊,為什麼伺服器沒有傳送FIN報文呢?我的伺服器使用的是嵌入 式的jetty,連線管理應該都是它幫我管理的,重啟了一下伺服器發現伺服器的CLOSE_WAIT開始的時候沒有出現,之後逐漸的上升,貌似隨著請求的 數量逐漸增長的,而我這邊的日誌也非常奇怪,我會在收到請求的時候列印日誌,然後在執行完畢的時候輸出一個accesslog資訊,發現日誌中有入口的請 求日誌,但是accessLog沒有增長,於是單步除錯了一下,發現了問題:一個servlet的執行走到主流程就走不下去了,阻塞在資料庫訪問那一步 上,具體表現就是獲取不到資料庫連線!

查看了一下程式碼,發現原來是自己建立連線,執行sql,完成之後沒有關閉連線,OMG,這麼愚蠢的錯誤,於是在所有的資料庫操作的最後加上如下的程式碼:

 
  1. finally {

  2. DbUtils.closeQuietly(conn);

  3. }

好了,既然問題能夠解決了,現在回頭來思考一下問題產生的具體步驟:首先,我這邊的大部分請求都需要查詢資料庫,我的資料庫連線池設定的最大連線數是 100,所以每一個請求建立了一個連線,等到100個請求就把連線池佔滿了,但是處理servlet的那個執行緒並沒有釋放這個連線,於是接下來的請求再去 建立資料庫連線的時候就會一直阻塞在那裡,這裡我所用的是DBCP作為連線池的,它的實現好像是使用apache的objectPool來實現的,如果沒 有可用的連線物件會導致執行緒等待,好了,servlet由於得不到資料庫連線而阻塞了,這個客戶端的請求就一直等待,客戶端使用httpclient設定 了5s的請求超時時間,那麼超時之後就會丟擲異常,關閉連線,關閉連線導致客戶端傳送了FIN報文,我這邊的TCP/IP返回了ACK報文,但是由於處理 請求的執行緒還處於阻塞的狀態,所以當前的連線狀態時CLOSE_WAIT。

警示:

 ●  程式碼一定要規範,尤其是在寫一些關於自願申請的部分,一定要在寫函式之前寫上註釋告訴自己別忘了釋放資源。
 ●  資料庫連線和訪問要設定超時時間,這點避免阻塞。

 ●  伺服器的執行緒數也需要設定,使得問題儘可能的出現。


原文釋出時間為:2018-11-14

本文作者:HARRIES

本文來自雲棲社群合作伙伴“Java雜記”,瞭解相關資訊可以關注“Java雜記”。