繼上次解決完mysql連接過多,導致的TIME_WAIT進程過多問題之后,最近這個現象再一次出現,並且依然和之前一樣嚴重。只不過這次出現問題的mysql 服務跟上次不一樣,上一次主要是mysql master server,而這一次是mysql slave server。所以這意味着,我們上次解決了部分問題,但沒有徹底解決,還存在一部分問題。所以這次徹底的把這個問題好好梳理一下。
再次確認一下TIME_WAIT進程的所屬服務:
sudo netstat -anp | grep TIME_WAIT | awk '{print $5}' | sort | uniq -c | sort -nr | less
之前排名第一的是mysql master ,其次為mysql slave, 再次為redis的行情數據服務。上一次我們解決了mysql master的問題。所以目前TIME_WAIT進程最多的是mysql slave的連接進程,其次為redis的行情數據服務連接進程。優先查mysql slave,因為slave是只讀庫,后端這邊用到的比較少,總共只有2個后端模塊用到了,停掉這兩個模塊的進程,發現TIME_WAIT沒有明顯變化,所以集中查前端的php邏輯,因為前端php代碼較多,所以簡單一點,先統計apache log中各項接口的請求次數,如圖:
橫軸是整點時間,縱軸是請求次數。很明顯,我們有兩個接口的請求次數異乎尋常的多,而且是在開盤時間迅猛上漲,收盤之后迅速回落,跟TIME_WAIT進程暴漲的時間完全吻合。至此,我們基本可以推斷,就是這兩個接口導致了大量的TIME_WAIT進程,從而導致端口資源被耗盡,新的連接請求失敗。
我們再來確認一下,為什么這兩個接口會導致大量的TIME_WAIT進程。簡單來說,每一個tcp連接關閉后,主動關閉方都會保留這個連接一段時間,這個時間內,這個連接的狀態是TIME_WAIT,端口資源不會被釋放。這個超時時間為2*MSL。RFC 793中規定MSL為2分鍾,實際由系統決定,通常在30-120s。這個網上有很多詳細解釋,這里不過多闡述。因為連接的關閉釋放有一定時間,並不是程序運行完就立刻釋放端口資源,所以當申請的連接進程較多的時候,端口資源就不夠用了。系統默認可用的端口資源:
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
總共才28232個可用端口,我們從日志中統計到高峰期每秒300多個http請求,每個請求都至少有一次tcp連接(mysql/redis/memcache),那么一分鍾差不多就是20000個連接。這種環境下,所以端口資源耗盡是很正常的事情。
至於解決方案,網上有一些詳細的參考文檔: http://blog.csdn.net/yunhua_lee/article/details/8146830, http://rhomobi.com/topics/47
基本上解決思路分為兩個方向:
1. 建立socket連接時設置SO_LINGER。或者是調整系統參數, 修改/etc/sysctrl.conf中的這幾項
# 擴大端口范圍,增加端口資源 net.ipv4.ip_local_port_range = 1024 65535 #開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0 net.ipv4.tcp_tw_reuse = 1 #開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0 net.ipv4.tcp_tw_recycle = 1
#服務端主動發起關閉后等待的超時時間
net.ipv4.tcp_fin_timeout = 5
#開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊
net.ipv4.tcp_syncookies = 1
這些調整基本上是能起到一個提高系統性能的作用,使得系統能承受更多的短連接並發。但設置的時候需要謹慎,后續要注意觀察,有些參數設置可能在極端情況下存在丟包、斷連或者連接不上的隱患。
2. 優化自己的系統,減少短連接次數
因為tcp連接關閉后是有必要保留一段時間TIME_WAIT狀態的,我們的目標不應該是簡單的縮短TIME_WAIT時間,而是應該從根本上去優化我們的系統架構設計,減少不必要的短連接請求。
再回到那兩個高頻的接口請求,這兩個接口實際上是兩個股票行情數據接口,為了保證行情的及時更新,客戶端會在開市期間每隔2s去刷新一次。所以從根本上講,這兩類請求是不應該走短連接的。但我們的網站又都是用php實現,所以沒法用到連接池一類的東西(貌似也可以通過第三方軟件做,但對服務性能可能有影響)。