【发布时间】:2013-12-30 01:49:43
【问题描述】:
我们的网络应用在 .Net Framework 4.0 中运行。 UI 通过 ajax 调用来调用控制器方法。
我们需要使用供应商提供的 REST 服务。我正在评估在 .Net 4.0 中调用 REST 服务的最佳方式。 REST 服务需要基本身份验证方案,并且它
可以返回 XML 和 JSON 格式的数据。不需要上传/下载大量数据,将来我也看不到任何东西。我查看了一些用于 REST 消费的开源代码项目,并没有发现任何价值来证明项目中的额外依赖是合理的。开始评估WebClient 和HttpClient。我从 NuGet 下载了 .Net 4.0 的 HttpClient。
我搜索了WebClient和HttpClient之间的区别,this site提到单个HttpClient可以处理并发调用,它可以重用解析的DNS、cookie配置和身份验证。我还没有看到我们可能因差异而获得的实际价值。
我做了一个快速的性能测试,以了解WebClient(同步调用)、HttpClient(同步和异步)的执行情况。结果如下:
对所有请求使用相同的 HttpClient 实例(最小 - 最大)
WebClient 同步:8 毫秒 - 167 毫秒
HttpClient 同步:3 毫秒 - 7228 毫秒
HttpClient 异步:985 - 10405 毫秒
为每个请求使用一个新的HttpClient(最小值 - 最大值)
WebClient 同步:4 毫秒 - 297 毫秒
HttpClient 同步:3 毫秒 - 7953 毫秒
HttpClient 异步:1027 - 10834 毫秒
代码
public class AHNData
{
public int i;
public string str;
}
public class Program
{
public static HttpClient httpClient = new HttpClient();
private static readonly string _url = "http://localhost:9000/api/values/";
public static void Main(string[] args)
{
#region "Trace"
Trace.Listeners.Clear();
TextWriterTraceListener twtl = new TextWriterTraceListener(
"C:\\Temp\\REST_Test.txt");
twtl.Name = "TextLogger";
twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;
ConsoleTraceListener ctl = new ConsoleTraceListener(false);
ctl.TraceOutputOptions = TraceOptions.DateTime;
Trace.Listeners.Add(twtl);
Trace.Listeners.Add(ctl);
Trace.AutoFlush = true;
#endregion
int batchSize = 1000;
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = batchSize;
ServicePointManager.DefaultConnectionLimit = 1000000;
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientAsync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientSync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
using (WebClient client = new WebClient())
{
Stopwatch sw = Stopwatch.StartNew();
byte[] arr = client.DownloadData(_url);
sw.Stop();
Trace.WriteLine("WebClient Sync " + sw.ElapsedMilliseconds);
}
});
Console.Read();
}
public static T GetDataFromWebClient<T>()
{
using (var webClient = new WebClient())
{
webClient.BaseAddress = _url;
return JsonConvert.DeserializeObject<T>(
webClient.DownloadString(_url));
}
}
public static void GetDataFromHttpClientSync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).Result;
var obj = JsonConvert.DeserializeObject<T>(
response.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Sync " + sw.ElapsedMilliseconds);
}
public static void GetDataFromHttpClientAsync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).ContinueWith(
(a) => {
JsonConvert.DeserializeObject<T>(
a.Result.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Async " + sw.ElapsedMilliseconds);
}, TaskContinuationOptions.None);
}
}
}
我的问题
- REST 调用在 3-4 秒内返回,这是可以接受的。调用 REST 服务是在控制器方法中启动的,这些方法从中调用 ajax 调用。首先,调用在不同的线程中运行,并且 不会阻止用户界面。那么,我可以坚持使用同步调用吗?
- 上面的代码是在我的localbox中运行的。在产品设置、DNS 和代理中
将涉及查找。使用
HttpClient比使用WebClient有什么优势吗? -
HttpClient的并发性是否优于WebClient?从测试结果中,我看到WebClient同步调用表现更好。 - 如果我们升级到 .Net 4.5,
HttpClient会是更好的设计选择吗?性能是关键的设计因素。
【问题讨论】:
-
您的测试对
GetDataFromHttpClientAsync不公平,因为它首先运行,其他调用受益于潜在的缓存数据(无论是在本地计算机上还是在您和目的地之间的任何透明代理上)和会更快。此外,在正确的条件下,var response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;可能会由于您耗尽线程池线程而导致死锁。您永远不应该阻塞依赖于 ThreadPool 线程中的线程池的活动,您应该await改为将线程返回到池中。 -
带有 Web API 客户端的 HttpClient 非常适合 JSON/XML REST 客户端。
-
@Scott Chamberlain - 感谢您的回复。由于所有测试调用都在 Parallel.Foreach 中运行,因此无法保证哪个会先运行。此外,如果对服务的第一次调用来自 GetDataFromHttpClientAsync ,则来自 GetDataFromHttpClientAsync 的所有后续调用都应该受益于缓存并运行得更快。我没有在结果中看到这一点。 rgd await,我们还在用4.0。我同意你的观点,同步方式的 HttpClient 会导致死锁,我将这个选项排除在我的设计考虑之外。
-
这里说几句HttpClient和WebClient的区别:blogs.msdn.com/b/henrikn/archive/2012/02/11/…
-
docs.microsoft.com/en-us/dotnet/api/… 建议在新开发中使用
HttpClient而不是WebClient。 .NET Framework 和 .NET Core 都是如此。
标签: c# .net rest webclient dotnet-httpclient