use*_*913 204 .net c# rest webclient dotnet-httpclient
我们的网络应用程序在.Net Framework 4.0中运行.UI通过ajax调用调用控制器方法.
我们需要从供应商处使用REST服务.我正在评估在.Net 4.0中调用REST服务的最佳方法.REST服务需要基本身份验证方案,它可以返回XML和JSON中的数据.没有要求上传/下载大量数据,我将来也看不到任何东西.我看了几个用于REST消费的开源代码项目,并没有找到任何值来证明项目中的额外依赖性.开始评估WebClient
和HttpClient
.我从NuGet下载了用于.Net 4.0的HttpClient.
我搜索了WebClient
和之间的差异,HttpClient
并且该网站提到单个HttpClient可以处理并发调用,它可以重用已解析的DNS,cookie配置和身份验证.我还没有看到由于差异我们可能获得的实用价值.
我做了一个快速的性能测试,以找到WebClient
(同步调用),HttpClient
(同步和异步)如何执行.以下是结果:
HttpClient
对所有请求使用相同的实例(min - max)
WebClient同步:8毫秒 - 167毫秒
HttpClient同步:3毫秒 - 7228毫秒
HttpClient异步:985 - 10405毫秒
HttpClient
为每个请求使用new (min - max)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
HttpClient
结束有什么好处WebClient
吗?HttpClient
并发好过WebClient
?从测试结果中,我看到 WebClient
同步调用表现更好.HttpClient
如果我们升级到.Net 4.5,将是一个更好的设计选择吗?性能是关键的设计因素.Ana*_*bhi 221
我生活在F#和Web API世界中.
Web API发生了很多好事,特别是以安全性的消息处理程序等形式.
我知道我的意见只有一个,但我只建议HttpClient
将来用于任何工作.也许有一些方法可以利用其他部分System.Net.Http
而不直接使用该程序集,但我无法想象这将如何起作用.
说到比较这两个
如果您使用的是.NET 4.5,请使用Microsoft为开发人员提供的HttpClient的async优度.HttpClient与HTTP的服务器端兄弟非常对称,即HttpRequest和HttpResponse.
更新:使用新HttpClient API的5个理由:
参考
C#5.0 Joseph Albahari
(Channel9 - Video Build 2013)
使用新的HttpClient API连接到Web服务的五大理由
WebClient vs HttpClient vs HttpWebRequest
Tim*_*ith 56
HttpClient是最新的API,它具有以下优点
如果您正在编写一个对其他Web服务进行REST调用的Web服务,那么您应该为所有REST调用使用异步编程模型,这样就不会遇到线程饥饿问题.您可能还想使用具有async/await支持的最新C#编译器.
注意:它不是更高性能的AFAIK.如果你创建一个公平的测试,它可能在某种程度上同样高效.
评估创建 HttpClient 的不同方式很重要,其中一部分是了解 HttpClientFactory。
我知道这不是一个直接的答案 - 但你最好从这里开始而不是new HttpClient(...)
到处结束。
首先,我不是 WebClient 与 HttpClient 的权威,特别是。其次,从你上面的评论来看,似乎表明 WebClient 是 Sync ONLY 而 HttpClient 两者都是。
我做了一个快速的性能测试,以了解 WebClient(同步调用)、HttpClient(同步和异步)的执行情况。这是结果。
我认为这是在考虑未来时的巨大差异,即长时间运行的进程、响应式 GUI 等(添加到框架 4.5 建议的好处 - 在我的实际经验中,在 IIS 上速度要快得多)
也许你可以用不同的方式来思考这个问题。WebClient
本质HttpClient
上是同一事物的不同实现。我建议在整个应用程序中使用IoC 容器实现依赖注入模式。您应该构建一个比低级别 HTTP 传输具有更高抽象级别的客户端接口。您可以编写同时使用和 的具体类,然后使用 IoC 容器通过 config 注入实现。WebClient
HttpClient
HttpClient
这将允许您在和 之间轻松切换,WebClient
以便您能够在生产环境中客观地进行测试。
所以问题如下:
如果我们升级到.Net 4.5,HttpClient 会是更好的设计选择吗?
实际上可以通过使用 IoC 容器在两个客户端实现之间切换来客观地回答。以下是您可能依赖的示例界面,其中不包含有关HttpClient
或 的任何详细信息WebClient
。
/// <summary>
/// Dependency Injection abstraction for rest clients.
/// </summary>
public interface IClient
{
/// <summary>
/// Adapter for serialization/deserialization of http body data
/// </summary>
ISerializationAdapter SerializationAdapter { get; }
/// <summary>
/// Sends a strongly typed request to the server and waits for a strongly typed response
/// </summary>
/// <typeparam name="TResponseBody">The expected type of the response body</typeparam>
/// <typeparam name="TRequestBody">The type of the request body if specified</typeparam>
/// <param name="request">The request that will be translated to a http request</param>
/// <returns></returns>
Task<Response<TResponseBody>> SendAsync<TResponseBody, TRequestBody>(Request<TRequestBody> request);
/// <summary>
/// Default headers to be sent with http requests
/// </summary>
IHeadersCollection DefaultRequestHeaders { get; }
/// <summary>
/// Default timeout for http requests
/// </summary>
TimeSpan Timeout { get; set; }
/// <summary>
/// Base Uri for the client. Any resources specified on requests will be relative to this.
/// </summary>
Uri BaseUri { get; set; }
/// <summary>
/// Name of the client
/// </summary>
string Name { get; }
}
public class Request<TRequestBody>
{
#region Public Properties
public IHeadersCollection Headers { get; }
public Uri Resource { get; set; }
public HttpRequestMethod HttpRequestMethod { get; set; }
public TRequestBody Body { get; set; }
public CancellationToken CancellationToken { get; set; }
public string CustomHttpRequestMethod { get; set; }
#endregion
public Request(Uri resource,
TRequestBody body,
IHeadersCollection headers,
HttpRequestMethod httpRequestMethod,
IClient client,
CancellationToken cancellationToken)
{
Body = body;
Headers = headers;
Resource = resource;
HttpRequestMethod = httpRequestMethod;
CancellationToken = cancellationToken;
if (Headers == null) Headers = new RequestHeadersCollection();
var defaultRequestHeaders = client?.DefaultRequestHeaders;
if (defaultRequestHeaders == null) return;
foreach (var kvp in defaultRequestHeaders)
{
Headers.Add(kvp);
}
}
}
public abstract class Response<TResponseBody> : Response
{
#region Public Properties
public virtual TResponseBody Body { get; }
#endregion
#region Constructors
/// <summary>
/// Only used for mocking or other inheritance
/// </summary>
protected Response() : base()
{
}
protected Response(
IHeadersCollection headersCollection,
int statusCode,
HttpRequestMethod httpRequestMethod,
byte[] responseData,
TResponseBody body,
Uri requestUri
) : base(
headersCollection,
statusCode,
httpRequestMethod,
responseData,
requestUri)
{
Body = body;
}
public static implicit operator TResponseBody(Response<TResponseBody> readResult)
{
return readResult.Body;
}
#endregion
}
public abstract class Response
{
#region Fields
private readonly byte[] _responseData;
#endregion
#region Public Properties
public virtual int StatusCode { get; }
public virtual IHeadersCollection Headers { get; }
public virtual HttpRequestMethod HttpRequestMethod { get; }
public abstract bool IsSuccess { get; }
public virtual Uri RequestUri { get; }
#endregion
#region Constructor
/// <summary>
/// Only used for mocking or other inheritance
/// </summary>
protected Response()
{
}
protected Response
(
IHeadersCollection headersCollection,
int statusCode,
HttpRequestMethod httpRequestMethod,
byte[] responseData,
Uri requestUri
)
{
StatusCode = statusCode;
Headers = headersCollection;
HttpRequestMethod = httpRequestMethod;
RequestUri = requestUri;
_responseData = responseData;
}
#endregion
#region Public Methods
public virtual byte[] GetResponseData()
{
return _responseData;
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
您可以在其实现中使用Task.Run
make异步运行。WebClient
依赖注入如果做得好,有助于缓解必须预先做出低级决策的问题。最终,了解真正答案的唯一方法是在现场环境中进行尝试,看看哪一个效果最好。很有可能这WebClient
对某些客户来说效果更好,而HttpClient
对另一些客户来说可能效果更好。这就是为什么抽象很重要。这意味着代码可以快速换入或更改配置,而无需更改应用程序的基本设计。
顺便说一句:还有许多其他原因表明您应该使用抽象而不是直接调用这些低级 API 之一。其中一个重要因素是单元可测试性。
2020 年不受欢迎的观点:
当谈到ASP.NET 应用程序时,我仍然更喜欢WebClient
,HttpClient
因为:
归档时间: |
|
查看次数: |
121521 次 |
最近记录: |