小心!TCP本機客戶端鏈接本機服務器

上週,在咱們進行性能測試的時候,發現了一個問題。
咱們的服務器上啓了一個redis服務端,偵聽0.0.0.0的1234端口,同處在本機的另一個進程會頻繁發起到該服務端的短鏈接,結果致使了兩個問題:
1.大量的TIME_WAIT狀態的鏈接;
2.發起鏈接的進程的CPU佔用率接近100%。
這兩個結果嚴重影響了咱們網關的性能,在分析具體緣由以前,首先作一個提倡,那就是:本機鏈接本機,首選UNIX域套接字而不是TCP!

緣由其實不須要數據分析,僅僅理論分析就夠了,前提是你要作Linux內核協議棧的IP層處理以及軟中斷調度有足夠的理解,固然,這些都很簡單。
       首先咱們來看看問題1。TIME_WAIT就很少說了,只要任何一端主動斷開鏈接,那麼它最終可能將會進入TIME_WAIT狀態,具體是否會進入在 Linux上取決於幾個因素,第一,你有沒有兩端開啓timestamps,若是開啓了,你有沒有在服務端開啓recycle,若是開啓了,那麼 TIME_WAIT套接字就會迅速消失,也就是說,想讓recycle起做用,你必定要開啓timestamps。若是沒有timestamps,那麼就 會有大量的TIME_WAIT狀態的套接字。
       在Linux內核協議棧的實現中,全部鏈接本機的數據流,其路由選擇最終都會到定向到loopback,若是你沒有綁定源IP地址,那麼源/目標IP地址 均爲127.0.0.1!若是服務端口是固定的,那麼最終會接受65535-1個鏈接,減1的緣由在於服務端已經bind了服務端口,所以客戶端不能再次 bind。這是合理的,由於按照四元組惟一性考慮,一個服務只能接受一個特定IP地址的65535個鏈接或者65534個鏈接,可是問題是,若是需求巨 大,這顯然不能知足要求,你要知道,做爲服務器而言,它要考慮的是總的最大併發鏈接數,一臺機器上同時發起6萬多個鏈接的可能性並不大,所以TCP在大多 數狀況下是合理,採用16bit的端口號剛恰好,由於協議頭不能太大,不然載荷率就會變小,這顯然是網絡傳輸所要求的,然而本機連本機時,並不須要網絡傳 輸,你想固然會認爲有多少需求就要都要知足,不過TCP並不適合這種場合。
       本機連本機,沒有網絡傳輸帶來的延遲,吞吐限制也僅限於本機資源利用,所以併發10萬甚至更多的需求都是合理的,但是TCP並不能知足,緣由就在於它只有 16bit的端口號,目標端口固定,同時只能有65534個鏈接。如何解決呢?咱們知道127.0.0.0/8都是屬於loopback的,咱們能夠採用 不一樣的源IP地址,若是想這麼作,有兩個選擇,那就是要麼客戶端bind源IP爲127.x.y.z,要麼SNAT成127.x.y.z,這樣就能夠接受 海量的鏈接需求了。可是這並非最終的解決方案,爲何非要用TCP呢?TCP原本就是爲網絡傳輸設計的,它的流控應對不一樣的主機,擁控應對反覆無常的網 絡,在本機,這些都不是問題,因此本機連本機,最好使用本機套接字,好比UNIX域套接字。
       再來看問題2,一個鏈接本機的TCP數據包最終到達了loopback的xmit發送函數,其中簡單的調度了本CPU上的一個軟中斷處理,而後會在下一次 中斷結束後調度其執行,這有很大概率是在當前發送進程的上下文中進行的,也就是說,發送進程在其上下文中進行了發送操做,而此時軟中斷借用了其上下文觸發 了接收操做,再而後,LOCK的開銷就很明顯,因爲大量的TW套接字的insert和delete,須要頻繁LOCK哈希表,這種開銷徹底記賬到了發送進程的名下,也是不公平的。
       注意,Linux內核中,softirq會在兩種上下文中執行,一種是硬件中斷後的任意上下文中,一種是每CPU一個內核線程的上下文中,後者會記賬給top命令的si百分比,前者則會記賬給任意被中斷的進程。

redis

相關文章
相關標籤/搜索