HttpClient - DNS更改不受尊重

时间:2017-11-24 17:32:34

标签: c# testing dotnet-httpclient

我已经阅读了一些文章以及使用静态HttpClient和解决方案的缺陷。其中一篇文章是 - http://byterot.blogspot.ca/2016/07/singleton-httpclient-dns.html

我已经实施了解决方案,并希望进行测试以确保文章提出的实际工作方式。

以下是我们试图避免的代码:

        Task.Run(async () =>
        {
            using (var httpClient = new HttpClient(new ApiHandler()))
            {
                var httpResponse = await httpClient.GetAsync("https://test.com/api/Tiers");
                var result = await httpResponse.Content.ReadAsStringAsync();
                Console.WriteLine(result);
            }
        }).ContinueWith(async task =>
        {
            await Task.Delay(5000);
            using (var httpClient = new HttpClient(new ApiHandler()))
            {
                var httpResponse = await httpClient.GetAsync("https://test.com/api/Tiers");
                var result = await httpResponse.Content.ReadAsStringAsync();
                Console.WriteLine(result);
            }
        }).ContinueWith(async task =>
        {
            await Task.Delay(10000);
            using (var httpClient = new HttpClient(new ApiHandler()))
            {
                var httpResponse = await httpClient.GetAsync("https://test.com/api/Tiers");
                var result = await httpResponse.Content.ReadAsStringAsync();
                Console.WriteLine(result);
            }
        });

当我查看fiddler中的流量时:

enter image description here

行为如预期。每个创建和部署都强制在请求和响应完成时建立连接。

使用HttpClient的正确和建议的方法是使它成为静态的:

    private static readonly HttpClient HttpClient = new HttpClient(new ApiHandler());
    static void Main()
    {
        ServicePointManager
            .FindServicePoint(new Uri("https://test.com"))
            .ConnectionLeaseTimeout = 1000; // 1 second
        ServicePointManager.DnsRefreshTimeout = 1000; // 1 second

        Task.Run(async () =>
        {
            var httpResponse = await HttpClient.GetAsync("https://test.com/api/Tiers");
            var result = await httpResponse.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }).ContinueWith(async task =>
        {
            await Task.Delay(5000); // delay 5 seconds
            var httpResponse = await HttpClient.GetAsync("https://test.com/api/Tiers");
            var result = await httpResponse.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }).ContinueWith(async task =>
        {
            await Task.Delay(10000); // delay 10 seconds
            var httpResponse = await HttpClient.GetAsync("https://test.com/api/Tiers");
            var result = await httpResponse.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        });

        Console.ReadKey();
    }

我期望与上一张图片具有相同的行为,但它看起来如下:

enter image description here

如果我然后添加HttpClient.DefaultRequestHeaders.ConnectionClose = true;,我会得到所需的结果,但这是我们想要避免的 - 对于每个创建连接的请求响应。

我对Fiddler的理想结果是正确的吗?或者我错过了设置ConnectionLeaseTimeout和/或DnsRefreshTimeout的内容?我真的很想测试这种行为,并确保在ServicePointManager上设置这些属性可以解决HttpClient静态实例的已知DNS问题。

1 个答案:

答案 0 :(得分:3)

Fiddler将自己注册为代理。默认情况下,它侦听127.0.0.1,端口8888.因此,要为您的案例获取正确的服务点对象,您必须执行此操作:

ServicePointManager.FindServicePoint(
    new Uri("https://test.com"), 
    new WebProxy(new Uri("http://127.0.0.1:8888")))
.ConnectionLeaseTimeout = 1000; // 1 second

现在你会看到正确的结果。请注意,它并不完全符合您的预期。第一个请求将打开新连接。第二个请求将打开新连接,即使已经过了5秒。相反,它会根据请求设置Connection: close标头,表示应该关闭连接。然后,下一个请求(第三个)将最终启动新连接。就ConnectionLeaseTimeout而言,这是预期的行为。