4

我正在尝试同步使用 HttpClient 但是当我发出许多并发请求时它停止工作。我写了两个测试,一个用于异步使用,一个用于同步使用。TestMethod总是在 4 秒后返回响应。异步测试工作正常。几乎所有请求在同步测试中都超时,只有大约 20 个最后的请求是成功的。我尝试使用单个HttpClient请求并HttpClient为每个新请求创建新实例。没有不同。也许这与这种僵局有关。

我正在将 VS2013 与面向 Framework 4.5 的 .NET Framework 4.5.1 一起使用。我通过 NuGet 获得 HttpClient:<package id="Microsoft.Net.Http" version="2.2.15" targetFramework="net45" />

我还不想HttpClient异步使用,因为这意味着我必须重写我的整个应用程序。任何想法我在这里做错了什么?

// all 800 requests complete successfully
[Test]
public async void Asyncmethod()
{
    var sw = new Stopwatch();
    sw.Start();

    var client = new HttpClient();
    client.Timeout = TimeSpan.FromSeconds(15);

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(async () =>
    {
        try
        {
            var swx = new Stopwatch();
            swx.Start();
            var response = await client.GetStringAsync("http://localhost:7002/TestMethod").ConfigureAwait(false);
            swx.Stop();
            Console.WriteLine(x + " " + response + " in " + swx.ElapsedMilliseconds + " ms.");
        }
        catch (Exception e)
        {
            Console.WriteLine(x + " Exception: " + e.Message);
        }
    })).ToArray();

    await Task.WhenAll(tasks);

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}

// almost all of 800 requests time out
[Test]
public void Syncmethod()
{
    var sw = new Stopwatch();
    sw.Start();

    var client = new HttpClient();

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() =>
    {
        try
        {
            var swx = new Stopwatch();
            swx.Start();
            var response = client.GetStringAsync("http://localhost:7002/TestMethod");
            if (response.Wait(15000))
            {
                swx.Stop();
                Console.WriteLine(x + " " + response.Result + " in " + swx.ElapsedMilliseconds + " ms.");
            }
            else
            {
                swx.Stop();
                Console.WriteLine(x + " timed out.");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(x + " Exception: " + e.Message);
        }
    })).ToArray();

    foreach (var task in tasks)
        task.Wait(60000);

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}
4

2 回答 2

8

我建议您使用awaitforHttpClient而不是Wait. 如果您真的想要同步请求,请考虑使用WebClient支持同步的方法。

也就是说,我相信您看到该行为的原因是由于ServicePointManager.DefaultConnectionLimit,这将限制对给定服务器的请求数量。如果您希望对同一服务器(同步或异步)有大量并发请求,则需要增加该限制(UI 应用程序默认为 2)。

于 2013-10-22T12:53:49.513 回答
2

是的,这似乎是僵局。

此代码完美运行

public static void Syncmethod()
    {
        var sw = new Stopwatch();
        sw.Start();

        var client = new HttpClient();

        var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() =>
        {

            var swx = new Stopwatch();
            swx.Start();
            var result =
            client.GetStringAsync("http://yandex.ru").ContinueWith(task =>
            {
                try
                {
                    swx.Stop();
                    Console.WriteLine(x + " " + task.Result + " in " + swx.ElapsedMilliseconds + " ms.");
                    return task.Result;
                }
                catch (Exception e)
                {
                    swx.Stop();
                    Console.WriteLine(x + " Exception: " + e.Message);
                    throw e;
                }
            }, TaskContinuationOptions.AttachedToParent);

        })).ToArray();

        foreach (var task in tasks)
            task.Wait(60000);

        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }

我猜你需要额外的逻辑来处理超时。例如,可能是在 15 秒内完成的另一项任务。

查看这篇文章HttpClient.GetAsync(...) never return when using await/async

如上所述,您可以通过简单的更改来挂起您的异步示例

var response = client.GetStringAsync("http://yandex.ru").GetAwaiter().GetResult();

当您编写异步代码时,永远不要使用块执行并等待结果。

于 2013-10-22T08:46:14.890 回答