簡體   English   中英

如何防止套接字/端口耗盡?

[英]How do I prevent Socket/Port Exhaustion?

我試圖通過使用跨多個線程的請求來對網站進行性能測試。 每個線程執行n次。 (在 for 循環中)

但是,我遇到了問題。 特別是具有內部異常的 WebException(“無法連接到遠程服務器”):

無法對套接字執行操作,因為系統缺少足夠的緩沖區空間或隊列已滿 127.0.0.1:52395

我試圖以每個線程 500 次迭代運行 100 個線程。

最初我在 System.Net 中使用HttpWebRequest向服務器發出 GET 請求。 目前我正在使用WebClient因為我認為每次迭代都在使用一個新的套接字(所以在短時間內有 100 * 500 個套接字)。 我假設 WebClient(每個線程實例化一次)只會使用一個套接字。

我不需要一次打開 50 000 個套接字,因為我想發送 GET 請求、接收響應並關閉套接字,將其釋放以供下一次循環迭代使用。 我明白這將是一個問題

然而,即使使用 WebClient,也會請求一堆套接字,導致一堆TIME_WAIT模式的套接字(使用 netstat 檢查)。 這會導致其他應用程序(如 Internet 瀏覽器)掛起並停止運行。

我可以用更少的迭代和/或更少的線程來運行我的測試,因為看起來套接字最終會退出這個 TIME_WAIT 狀態。 但是,這不是解決方案,因為它沒有充分測試 Web 服務器的能力。

問題:

如何在每次線程迭代后顯式關閉套接字(從客戶端)以防止 TIME_WAIT 狀態和套接字耗盡?

代碼:

包裝 HttpRequest 的類

編輯:將WebClient 包裝在 using 中,因此每次迭代都會實例化、使用和處理一個新的 WebClient。 問題仍然存在。

  public sealed class HttpGetTest : ITest {
    private readonly string m_url;

    public HttpGetTest( string url ) {          
        m_url = url;
    }

    void ITest.Execute() {
        using (WebClient webClient = new WebClient()){
            using( Stream stream = webClient.OpenRead( m_url ) ) {          
            }
        }
    }
}

創建新線程的 ThreadWrapperClass 部分:

public void Execute() {
    Action Hammer = () => {
        for( int i = 1; i <= m_iterations; i++ ) {
            //Where m_test is an ITest injected through constructor
            m_test.Execute();
        }       
    };
    ThreadStart work = delegate {
        Hammer();
    };
    Thread thread = new Thread( work );
    thread.Start();
}

你明白TIME_WAIT的目的嗎? 在此期間,重用端口是不安全的,因為前一個事務中丟失的數據包(已成功重新傳輸)可能尚未在該時間段內傳送。

您可能可以在注冊表中的某個地方對其進行調整,但我懷疑這是否是明智的下一步。

我在測試環境中創建真實負載的經驗證明非常令人沮喪。 當然,從 localhost 運行負載測試器絕不是現實的,而且我使用 .net http api 進行的大多數網絡測試似乎在客戶端中比服務器本身需要更多的咕嚕聲。

因此,最好轉移到第二台機器來在您的服務器上產生負載……但是,國內路由設備很少能勝任支持接近數量的任何連接的工作,這會在編寫良好的服務器上造成任何類型的負載應用程序,所以現在您還需要升級您的路由/交換設備!

最后,我在 .net Http 客戶端 API 周圍遇到了一些非常奇怪和意外的性能問題。 歸根結底,他們都使用 HttpWebRequest 來完成繁重的工作。 IMO 它遠沒有達到它所能達到的性能。 DNS 是同步的,即使在異步調用 API 時也是如此(盡管如果您只從單個主機請求,這不是問題),並且在持續使用后 CPU 使用率會逐漸上升,直到客戶端變為 CPU 受限而不是 IO 受限。 如果您希望生成持續且繁重的負載,那么任何依賴 HttpWebRequest 的請求密集型應用程序都是 IMO 一項虛假投資。

總而言之,這是一項非常棘手的工作,最終只能在野外證明,除非您有足夠的現金購買更好的設備。

[提示:我使用異步 Socket apis 和第 3 方 DNS 客戶端庫編寫的自己的客戶端獲得了更好的性能]

問:如何顯式關閉套接字...以防止 TIME_WAIT 狀態?

A:老兄,TIME_WAIT 是不可或缺的——而且很重要! - TCP/IP 本身的一部分!

可以調整操作系統以減少 TIME_WAIT(可能會產生負面影響)。

您可以調整操作系統以增加 #/ephemeral 端口:

這是一個關於為什么 TIME_WAIT 存在......以及為什么它是一件好事的鏈接:

這不是關閉套接字或釋放應用程序中的資源的問題。 TIME _WAIT 是已釋放套接字上的 TCP 堆棧超時,以防止重新使用它們,直到從先前連接到該套接字的任何數據包“遺留”到該套接字幾乎不可能不過期為止。

出於測試目的,您可以將等待時間從默認值(幾分鍾,AFAIK)減少到較小的值。 在對服務器進行負載測試時,我將其設置為 6 秒。

它在注冊表中的某個地方 - 如果你谷歌,你會找到它。

找到了:

更改 TIME_WAIT 延遲

看起來您並沒有強迫您的 WebClient 擺脫它已分配的資源。 您正在對返回的流執行使用,但您的 WebClient 仍然有資源。

要么將您的 WebClient 實例包裝在 using 塊中,要么在完成從 URL 讀取后手動調用 dispose 。

試試這個:

public sealed class HttpGetTest : ITest {
    private readonly string m_url;

    public HttpGetTest( string url ) {
        m_url = url;        
    }

    public void ITest.Execute() {
        using( var m_webClient = new WebClient())
        {
            using( Stream stream = m_webClient.OpenRead( m_url ) ) 
            {

            }
        }
    }
}

你不需要搞亂 TIME_WAIT 來完成你想要的。

問題是每次調用 Execute() 時都在處理 WebClient。 當你這樣做時,你關閉了與服務器的套接字連接,TCP 端口在 TIME_WAIT 期間保持忙碌。

更好的方法是在 HttpGetTest 類的構造函數中創建 WebClient 並在整個測試中重用相同的對象。

默認情況下,WebClient 使用保持活動狀態,並將為其所有請求重用相同的連接,因此在您的情況下,將只有 100 個打開的連接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM