天天看點

聊一聊 tcp/ip 在.NET故障分析的重要性

作者:一線碼農聊技術

一:背景

1. 講故事

這段時間分析了幾個和網絡故障有關的.NET程式之後,真的越來越體會到計算機基礎課的重要,比如 計算機網絡 課,如果沒有對 tcpip協定 的深刻了解,解決這些問題真的很難,因為你隻能在高層做黑盒測試,你無法看到 tcp 層面的握手和psh通訊。

這篇我們通過兩個小例子來了解一下 tcp 協定在故障分析中的作用。

二:tcp協定的兩個小例子

1. 程式突然大量逾時

這個故事起源于一位朋友遇到的問題:

起初程式跑的一直都是好好的,但會有偶發性突然無法通路,奇怪的是在故障時手工通路域名時又是正常的,後面又莫名奇怪的好了,請問這是怎麼回事?

這種問題朋友雖然抓了dump,但在dump中尋找問題很難,因為大機率是在 http 通訊中出了問題,需要用類似 wireshark 去做流量監控,最後發現的原因是代理伺服器偶發的抽風,導緻 C# 的 HttpClient 無法通路。

為了友善示範,這裡用一段簡單的測試代碼。

  1. WebAPI 代碼

建立一個 WebApi 骨架代碼,然後部署 Windows 虛拟機上。

[HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }

           

并且在 appsetttings.json 中配置對外端口為 80。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://0.0.0.0:80"
      }
    }
  }
}

           
  1. Client 的 HttpClient

這裡面我用 hosts 做了虛拟機 192.168.25.133 myproxy.com 的映射,然後通過域名的方式通路。

internal class Program
    {
        public static HttpClient client = new HttpClient(new HttpClientHandler()
        {
            Proxy = new WebProxy("http://myproxy.com")
        });

        static async Task Main(string[] args)
        {
            for (int i = 0; i < 100000; i++)
            {
                try
                {
                    // 發送 GET 請求
                    HttpResponseMessage response = await client.GetAsync("http://youtube.com/WeatherForecast");

                    // 檢查響應狀态碼
                    response.EnsureSuccessStatusCode();

                    // 讀取響應内容
                    string responseBody = await response.Content.ReadAsStringAsync();

                    // 輸出響應内容
                    Console.WriteLine(responseBody);

                    await Task.Delay(1000);
                }
                catch (HttpRequestException e)
                {
                    Console.WriteLine(#34;{DateTime.Now} HTTP 請求異常:{e.Message} {e.GetType().Name}");
                }
            }

        }
    }

           

打開 wireshark 進行流量監聽,将程式運作起來,發現一切都是那麼太平,截圖如下:

聊一聊 tcp/ip 在.NET故障分析的重要性

由于某些原因,代理伺服器出了問題,這裡用 關閉的方式來模拟,再次觀察 wireshark 可以發現,沒有收到伺服器對154号包的響應,client 這邊根據 RTO=1s 進行重試。

聊一聊 tcp/ip 在.NET故障分析的重要性

2. DNS解析到的IP無法通路

有些朋友程式出現了卡死,原因在于設定了很長的 Timeout,這種 Timeout 挺有意思,域名能夠通過 DNS 解析到 IP,但 IP 無法被通路到,導緻 client 這邊在不斷的重試,直到 timeout 的時限到時抛出異常。

接下來還是用 HttpClient 做一個小例子,直接通路 youtube.com ,參考如下代碼:

static async Task Main(string[] args)
        {
            HttpClient client = new HttpClient();

            for (int i = 0; i < 100000; i++)
            {
                try
                {
                    // 發送 GET 請求
                    HttpResponseMessage response = await client.GetAsync("http://youtube.com");

                    // 檢查響應狀态碼
                    response.EnsureSuccessStatusCode();

                    // 讀取響應内容
                    string responseBody = await response.Content.ReadAsStringAsync();

                    // 輸出響應内容
                    Console.WriteLine(responseBody);

                    await Task.Delay(1000);
                }
                catch (HttpRequestException e)
                {
                    Console.WriteLine(#34;{DateTime.Now} HTTP 請求異常:{e.Message} {e.GetType().Name}");
                }
            }
        }

           

打開 wireshark 啟動監控,然後将程式運作起來,截圖如下:

聊一聊 tcp/ip 在.NET故障分析的重要性

從卦中可以看到 client 發起了一個 DNS 查詢,DNS伺服器查詢到 youtube.com 所對應的 IP 是 104.244.46.85,接下來應該就是 client 對這個 ip 發起 握手請求,截圖如下:

聊一聊 tcp/ip 在.NET故障分析的重要性

從圖中資訊看,真的很尬尴,有如下兩點資訊:

  • client 發起了 SYN 請求,結果沒人鳥它,沒人鳥主要是因為路徑上的防火牆把這個 SYN ACK 給沒收了。
  • client 端按照 1s,2s,4s,8s 的RTO計時器逾時進行重試,直到 HttpClient 等不及抛 TimeoutException 異常。

三:總結

人是活在錯綜複雜的關系網裡,同樣程式也是,要想解決更多的.NET程式故障,對 tcp/ip 體系知識的了解也同樣必不可少。