• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評論 - 101, 引用 - 0
            數據加載中……

            從問題看本質: 研究TCP close_wait的內幕

             * @author: ahuaxuan

             * @date: 2010-4-30

             */

             

            最近遇到的一個關于socket.close的問題,在某個應用服務器出現的狀況(執行netstat -np | grep tcp): 

            tcp        0      0 10.224.122.16:50158         10.224.112.58:8788          CLOSE_WAIT

            tcp        0      0 10.224.122.16:37655         10.224.112.58:8788          CLOSE_WAIT

            tcp        1      0 127.0.0.1:32713             127.0.0.1:8080              CLOSE_WAIT

            tcp       38      0 10.224.122.16:34538         10.224.125.42:443           CLOSE_WAIT

            tcp       38      0 10.224.122.16:33394         10.224.125.42:443           CLOSE_WAIT

            tcp        1      0 10.224.122.16:18882         10.224.125.10:80            CLOSE_WAIT

            tcp        1      0 10.224.122.16:18637         10.224.125.10:80            CLOSE_WAIT

            tcp        1      0 10.224.122.16:19655         10.224.125.12:80            CLOSE_WAIT

            ........................................

             

            總共出現了200個CLOSE_WAIT的socket.而且這些socket長時間得不到釋放.下面我們來看看為什么會出現這種大量socket的CLOSE_WAIT情況

             

            首先我們要搞清楚的是,這個socket是誰發起的,我們可以看到122.16這臺機器開了很多端口,而且端口號都很大,125.12 或者125.10上的端口都是很常見服務器端口,所以122.16上這么多CLOSE_WAIT

            的socket是由122.16開啟的,換句話說這臺機器是傳統的客戶端,它會主動的請求其他機器的服務端口.

             

            要搞清楚為什么會出現CLOSE_WAIT,那么首先我們必須要清楚CLOSE_WAIT的機制和原理.

             

            假設我們有一個client, 一個server.

             

            當client主動發起一個socket.close()這個時候對應TCP來說,會發生什么事情呢?如下圖所示.

             

            ?

             

             

            client首先發送一個FIN信號給server, 這個時候client變成了FIN_WAIT_1的狀態, server端收到FIN之后,返回ACK,然后server端的狀態變成了CLOSE_WAIT.

            接著server端需要發送一個FIN給client,然后server端的狀態變成了LAST_ACK,接著client返回一個ACK,然后server端的socket就被成功的關閉了.

             

            從這里可以看到,如果由客戶端主動關閉一鏈接,那么客戶端是不會出現CLOSE_WAIT狀態的.客戶端主動關閉鏈接,那么Server端將會出現CLOSE_WAIT的狀態.

            而我們的服務器上,是客戶端socket出現了CLOSE_WAIT,由此可見這個是由于server主動關閉了server上的socket.

             

            那么當server主動發起一個socket.close(),這個時候又發生了一些什么事情呢.

            ?

             

            從圖中我們可以看到,如果是server主動關閉鏈接,那么Client則有可能進入CLOSE_WAIT,如果Client不發送FIN包,那么client就一直會處在CLOSE_WAIT狀態(后面我們可以看到有參數可以調整這個時間).

             

            那么現在我們要搞清楚的是,在第二中場景中,為什么Client不發送FIN包給server.要搞清楚這個問題,我們首先要搞清楚server是怎么發FIN包給client的,其實server就是調用了

            socket.close方法而已,也就是說如果要client發送FIN包,那么client就必須調用socket.close,否則就client就一直會處在CLOSE_WAIT(但事實上不同操作系統這點的實現還不一樣,

            在ahuaxuan(ahuaxuan.iteye.com)的例子中也出現了這樣的case).

             

            下面我們來做幾個實驗

            實驗一:

            環境:

            服務器端:win7+tomcat,tomcat的keep-alive的時間為默認的15s.

            客戶端:mac os

            實驗步驟:服務器啟動后,客戶端向服務器發送一個get請求,然后客戶端阻塞,等待服務器端的socket超時.通過netstat -np tcp可以看到的情況是發送get請求時,服務器和客戶端鏈接是ESTABLISHED, 15s之后,客戶端變成了CLOSE_WAIT,而服務器端變成了FIN_WAIT_2.這一點也在我們的預料之中,而這個時候由于客戶端線程阻塞,客戶端socket空置在那里,不做任何操作,2分鐘過后,這個鏈接不管是在win7上,還是在mac os都看不到了.可見,FIN_WAIT_2或者CLOSE_WAIT有一個timeout.在后面的實驗,可以證明,在這個例子中,其實是FIN_WAIT_2有一個超時,一旦過了2分鐘,那么win7會發一個RST給mac os要求關閉雙方的socket.

             

            實驗二

            服務器端:ubuntu9.10+tomcat,tomcat的keep-alive的時間為默認的15s.

            客戶端:mac os

            實驗步驟:服務器啟動后,客戶端向服務器發送一個get請求,然后客戶端阻塞,等待服務器端的socket超時.通過netstat -np tcp(ubuntu使用netstat -np|grep tcp)可以看到的情況是發送get請求時,服務器和客戶端鏈接是ESTABLISHED, 15s之后,客戶端變成了CLOSE_WAIT,而服務器端變成了FIN_WAIT_2.這一點也也在我們的預料之中,而這個時候由于客戶端線程阻塞,客戶端socket空置在那里,不做任何操作,1分鐘過后,ubuntu上的那個socket不見了,但是mac os上的socket還在,而且還是CLOSE_WAIT,這說明,FIN_WAIT_2確實有一個超時時間,win7上的超時操作可以關閉mac os上的socket,而ubuntu上的FIN_WAIT_2超時操作卻不能關閉mac os上的socket(其狀一直是CLOSE_WAIT).

             

            實驗三

            服務器端:mac os+tomcat,tomcat的keep-alive的時間為默認的15s.

            客戶端:mac os

            實驗步驟:服務器啟動后,客戶端向服務器發送一個get請求,然后客戶端阻塞,等待服務器端的socket超時.通過netstat -np tcp可以看到的情況是發送get請求時,服務器和客戶端鏈接是ESTABLISHED, 15s之后,客戶端變成了CLOSE_WAIT,而服務器端變成了FIN_WAIT_2.這一點也在我們的預料之中,而這個時候由于客戶端線程阻塞,客戶端socket空置在那里,不做任何操作,4分鐘過后,mac os服務器端上的那個socket不見了,但是mac os客戶端上的socket還在,而且還是CLOSE_WAIT,這說明,FIN_WAIT_2確實有一個超時時間,win7上的超時操作可以關閉mac os上的socket,而ubuntu和mac os上的FIN_WAIT_2超時操作卻不能關閉mac os上的socket.

             

             

             

            總結, 當服務器的內核不一樣上FIN_WAIT_2的超時時間和操作是不一樣的.

            經查:控制FIN_WAIT_2的參數為:

            /proc/sys/net/ipv4/tcp_fin_timeout

            如 果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。對端可以出錯并永遠不關閉連接,甚至意外當機。缺省值是60秒。2.2 內核的通常值是180秒,你可以按這個設置,但要記住的是,即使你的機器是一個輕載的WEB服務器,也有因為大量的死套接字而內存溢出的風險,FIN- WAIT-2的危險性比FIN-WAIT-1要小,因為它最多只能吃掉1.5K內存,但是它們的生存期長些。參見tcp_max_orphans。

             

            實驗四

            服務器端:ubuntu9.10+tomcat,tomcat的keep-alive的時間為默認的15s.

            客戶端:mac os

            實驗步驟:服務器啟動后,客戶端向服務器發送一個get請求,然后關閉客戶端關閉socket.通過netstat -np tcp可以看到的情況是發送get請求時,服務器和客戶端鏈接是ESTABLISHED, 客戶端拿到數據之后,客戶端變成了TIME_WAIT,而服務器端變成了已經看不到這個socket了.這一點也也在我們的預料之中,誰主動關閉鏈接,那么誰就需要進入TIME_WAIT狀態(除非他的FIN_WAIT_2超時了),大約1分鐘之后這個socket在客戶端也消失了.

             

            實驗證明TIME_WAIT的狀態會存在一段時間,而且在這個時間端里,這個FD是不能被回收的.

             

            但是我們的問題是客戶端有很多CLOSE_WAIT,而且我們的服務器不是windows,而是linux,所以CLOSE_WAIT有沒有超時時間呢,肯定有,而且默認情況下這個超時時間應該是比較大的.否則不會一下子看到兩百個CLOSE_WAIT的狀態.

             

            客戶端解決方案:

             

            1.由于socket.close()會導致FIN信號,而client的socket CLOSE_WAIT就是因為該socket該關的時候,我們沒有關,所以我們需要一個線程池來檢查空閑連接中哪些進入了超時狀態(idleTIME),但進入超時

            的socket未必是CLOSE_WAIT的狀態的.不過如果我們把空閑超時的socket關閉,那么CLOSE_WAIT的狀態就會消失.(問題:像HttpClient這樣的工具包中,如果要檢查鏈接池,那么則需要鎖定整個池,而這個時候,用戶請求獲取connection的操作只能等待,在高并發的時候會造成程序響應速度下降,具體參考IdleConnectionTimeoutThread.java(HttpClient3.1))

             

            2.經查,其實有參數可以調整CLOSE_WAIT的持續時間,如果我們改變這個時間,那么可以讓CLOSE_WAIT只保持很短的時間(當然這個參數不只作用在CLOSE_WAIT上,縮短這個時間可能會帶來其他的影響).在客戶端機器上修改如下:

            sysctl -w net.ipv4.tcp_keepalive_time=60(缺省是2小時,現在改成了60秒)

            sysctl -w net.ipv4.tcp_keepalive_probes=2

            sysctl -w net.ipv4.tcp_keepalive_intvl=2

            我們將CLOSE_WAIT的檢查時間設置為30s,這樣一個CLOSE_WAIT只會存在30S.

             

            3. 當然,最重要的是我們要檢查客戶端鏈接的空閑時間,空閑時間可以由客戶端自行定義,比如idleTimeout,也可由服務器來決定,服務器只需要每次在response.header中加入一個頭信息,比如說名字叫做timeout頭,當然一般情況下我們會用keep-alive這個頭字段, 如果服務器設置了該字段,那么客戶端拿到這個屬性之后,就知道自己的connection最大的空閑時間,這樣不會由于服務器關閉socket,而導致客戶端socket一直close_wait在那里.

             

            服務器端解決方案

             

            4.前面講到客戶端出現CLOSE_WAIT是由于服務器端Socket的讀超時,也是TOMCAT中的keep-alive參數.那么如果我們把這個超時時間設置的長點,會有什么影響?

            如果我們的tomcat既服務于瀏覽器,又服務于其他的APP,而且我們把connection的keep-alive時間設置為10分鐘,那么帶來的后果是瀏覽器打開一個頁面,然后這個頁面一直不關閉,那么服務器上的socket也不能關閉,它所占用的FD也不能服務于其他請求.如果并發一高,很快服務器的資源將會被耗盡.新的請求再也進不來. 那么如果把keep-alive的時間設置的短一點呢,比如15s? 那么其他的APP來訪問這個服務器的時候,一旦這個socket, 15s之內沒有新的請求,那么客戶端APP的socket將出現大量的CLOSE_WAIT狀態.

            所以如果出現這種情況,建議將你的server分開部署,服務于browser的部署到單獨的JVM實例上,保持keep-alive為15s,而服務于架構中其他應用的功能部署到另外的JVM實例中,并且將keep-alive的時間設置的更

            長,比如說1個小時.這樣客戶端APP建立的connection,如果在一個小時之內都沒有重用這條connection,那么客戶端的socket才會進入CLOSE_WAIT的狀態.針對不同的應用場景來設置不同的keep-alive時間,可以幫助我們提高程序的性能.

             

            5.如果我們的應用既服務于瀏覽器,又服務于其他的APP,那么我們還有一個終極解決方案.

            那就是配置多個connector, 如下:

            <!-- for browser -->

             <Connector port="8080" protocol="HTTP/1.1" 

                           connectionTimeout="20000" 

                           redirectPort="8443" />

             

            <!-- for other APP -->

            <Connector port="8081" protocol="HTTP/1.1" 

                           connectionTimeout="20000" 

                           redirectPort="8443" keepAliveTimeout="330000" />

             

            訪問的時候,瀏覽器使用8080端口,其他的APP使用8081端口.這樣可以保證瀏覽器請求的socket在15s之內如果沒有再次使用,那么tomcat會主動關閉該socket,而其他APP請求的socket在330s之內沒有使用,才關閉該socket,這樣做可以大大減少其他APP上出現CLOSE_WAIT的幾率.

             

            你一定會問,如果我不設置keepAliveTimeout又怎么樣呢,反正客戶端有idleTimeout,客戶端的close_wait不會持續太長時間,請注意看上圖中標紅的地方,一個是close_wait,還有一個是time_wait狀態,也就是說誰主動發起請求,那么它將會最終進入time_wait狀態,據說windows上這個time_wait將持續4分鐘,我在linux上的測試表明,linux上它大概是60s左右,也就是說高并發下,也就是服務器也需要過60s左右才能真正的釋放這個FD.所以我們如果提供http服務給其他APP,那么我們最好讓客戶端優先關閉socket,也就是將客戶端的idleTimeout設置的比server的keepalivetimeout小一點.這樣保證time_wait出現在客戶端. 而不是資源較為緊張的服務器端.

             

            總結:

                   本文中ahuaxuan給大家揭示了TCP層client和server端socket關閉的一般流程,并且指出異常情況下client和server端各自會發生的情況,包含了在不同平臺上出現了的不同情況, 同時說明了在應用層上我們可以做什么樣的邏輯來保證socket關閉時對server端帶來最小的影響.

             

             

             

            下面是網上找到的一些資料:

             

             寫道



            /proc/sys/net/ipv4/tcp_keepalive_time
當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時。
            
/proc/sys/net/ipv4/tcp_keepalive_intvl
當探測沒有確認時,重新發送探測的頻度。缺省是75秒。
            
/proc/sys/net/ipv4/tcp_keepalive_probes
在認定連接失效之前,發送多少個TCP的keepalive探測包。缺省值是9。這個值乘以tcp_keepalive_intvl之后決定了,一個連接發送了keepalive之后可以有多少時間沒有回應。


            /proc/sys/net/ipv4/tcp_max_orphans
            系 統中最多有多少個TCP套接字不被關聯到任何一個用戶文件句柄上。如果超過這個數字,孤兒連接將即刻被復位并打印出警告信息。這個限制僅僅是為了防止簡單的DoS攻擊,你絕對不能過分依靠它或者人為地減小這個值,更應該增加這個值(如果增加了內存之后)。This limit exists only to prevent simple DoS attacks, you _must_ not rely on this or lower the limit artificially, but rather increase it (probably, after increasing installed memory), if network conditions require more than default value, and tune network services to linger and kill such states more aggressively. 讓我再次提醒你:每個孤兒套接字最多能夠吃掉你64K不可交換的內存。

            /proc/sys/net/ipv4/tcp_orphan_retries
            本端試圖關閉TCP連接之前重試多少次。缺省值是7,相當于50秒~16分鐘(取決于RTO)。如果你的機器是一個重載的WEB服務器,你應該考慮減低這個值,因為這樣的套接字會消耗很多重要的資源。參見tcp_max_orphans。

            /proc/sys/net/ipv4/tcp_max_syn_backlog
            記 錄的那些尚未收到客戶端確認信息的連接請求的最大值。對于有128M內存的系統而言,缺省值是1024,小內存的系統則是128。如果服務器不堪重負,試 試提高這個值。注意!如果你設置這個值大于1024,最好同時調整include/net/tcp.h中的TCP_SYNQ_HSIZE,以保證 TCP_SYNQ_HSIZE*16 ≤tcp_max_syn_backlo,然后重新編譯內核。

            /proc/sys/net/ipv4/tcp_max_tw_buckets
            系 統同時保持timewait套接字的最大數量。如果超過這個數字,time-wait套接字將立刻被清除并打印警告信息。這個限制僅僅是為了防止簡單的 DoS攻擊,你絕對不能過分依靠它或者人為地減小這個值,如果網絡實際需要大于缺省值,更應該增加這個值(如果增加了內存之后)。

            posted on 2012-08-27 16:39 tqsheng 閱讀(211) 評論(0)  編輯 收藏 引用

            久久青青草原亚洲av无码| 久久久久久九九99精品| 中文成人无码精品久久久不卡 | 久久久久亚洲AV成人网人人网站| 久久夜色精品国产亚洲| 国产精品一久久香蕉国产线看| 亚洲一区中文字幕久久| 久久久久久久久久久久久久| 91久久精一区二区三区大全| 亚洲国产成人久久笫一页| 久久97精品久久久久久久不卡| 亚洲性久久久影院| 久久亚洲高清观看| 久久人妻少妇嫩草AV无码专区| 久久久久无码专区亚洲av| 久久国产精品一区二区| 精品伊人久久大线蕉色首页| 久久精品国产91久久综合麻豆自制| 久久人人青草97香蕉| 久久夜色精品国产亚洲| 久久婷婷五月综合国产尤物app| 久久久久国产一级毛片高清板| 久久丫精品国产亚洲av| 国产精品美女久久福利网站| 国产精品九九久久免费视频 | 超级97碰碰碰碰久久久久最新| 精品综合久久久久久97超人| 少妇人妻88久久中文字幕| 国产精品一区二区久久精品涩爱 | 漂亮人妻被中出中文字幕久久| 大蕉久久伊人中文字幕| 一本大道久久a久久精品综合 | 国产精品久久久久久久人人看| 国产亚洲成人久久| 91精品国产综合久久香蕉 | 国产精品一区二区久久精品涩爱 | 一级做a爰片久久毛片16| 久久精品国产免费一区| 天天久久狠狠色综合| 激情久久久久久久久久| 精品久久久久久久中文字幕|