同步并发 HttpClient 使用
Posted
技术标签:
【中文标题】同步并发 HttpClient 使用【英文标题】:Synchronous Concurrent HttpClient Usage 【发布时间】:2013-10-22 04:57:30 【问题描述】:我正在尝试同步使用 HttpClient,但是当我发出许多并发请求时它停止工作。我写了两个测试,一个用于异步使用,一个用于同步使用。 TestMethod
总是在 4 秒后返回响应。异步测试工作正常。几乎所有请求在同步测试中都超时,只有大约 20 个最后的请求是成功的。我尝试使用单个 HttpClient
请求并为每个新请求创建新的 HttpClient
实例。没有不同。可能和this deadlock有关。
我正在将 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);
【问题讨论】:
【参考方案1】:我建议您使用await
代替HttpClient
而不是Wait
。如果您确实想要同步请求,请考虑使用支持同步方法的WebClient
。
也就是说,我相信您看到这种行为的原因是由于ServicePointManager.DefaultConnectionLimit
,它会限制对给定服务器的请求数量。如果您希望对同一服务器有大量并发请求(同步或异步),则需要增加该限制(UI 应用程序默认为 2)。
【讨论】:
await
- 在所有问题都被标记后 C#-5.0
问题是,如果没有超时,则没有一个请求完成。只有在 780 由于超时而被取消后,我才收到大约 20 个已完成的请求。
使用 await 不是一个选项,我也必须重写其他所有内容。是的,WebClient
完美运行。但是我还是不明白为什么HttpClient
挂了。
DefaultConnectionLimit
是一个很好的挂起理由。默认值为 2,这意味着即使获得 20 个已完成的请求也不错。只是增加限制【参考方案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 returns when using await/async
如上所述,您可以通过简单的更改来挂起您的异步示例
var response = client.GetStringAsync("http://yandex.ru").GetAwaiter().GetResult();
当您编写异步代码时,永远不要使用阻止执行并等待结果。
【讨论】:
首先,在您的示例中,如果请求足够长,任务将在请求完成之前完成。其次,我将如何编写一个使用ContinueWith
从响应中返回某些内容的函数?
已更新。但是您必须重写代码才能使用 Task以上是关于同步并发 HttpClient 使用的主要内容,如果未能解决你的问题,请参考以下文章
Binance API 和 Angular 4 httpClient