问题描述
我正在尝试在我的 Xamarin.Forms
移动应用程序中使用 HttpClient 为网络服务创建层.
I am trying to create layer for webservice using HttpClient in my Xamarin.Forms
mobile app.
- 没有单例模式
- 使用单例模式
在 first 方法中,我在每个新请求中创建新的 http 客户端对象通过移动应用程序.
in first approach i am creating new http client object in each new request madeby mobile applicaiton.
这是我的代码
public HttpClient GetConnection()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(baseAddress);
httpClient.Timeout = System.TimeSpan.FromMilliseconds(timeout);
return httpClient;
}
发布请求代码
public async Task<TResult> PostAsync<TRequest, TResult>(String url, TRequest requestData)
{
HttpClient client = GetConnection();
String responseData = null;
if (client != null)
{
String serializedObject = await Task.Run(() => JsonConvert.SerializeObject(requestData, _jsonSerializerSettings));
var jsonContent = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(new Uri(url, UriKind.Relative), jsonContent);
responseData = await HandleResponse(response);
return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _jsonSerializerSettings));
}
else
{
throw new NullReferenceException("NullReferenceException @ PostAsync httpclient is null WebRequest.cs");
}
}
客户端将使用以下代码来执行请求
client will use following code to execute request
new LoginService(new WebRequest()).UserLogin(userRequest);
实现IWebRequest
_webRequest.PostAsync<UserRequest,bool>(Constants.USER_LOGIN, userRequest);
在第二种方法中,我在每个新请求中重用相同的 http 客户端对象在这里,我的单例类也是线程安全的.
in second approach i am reusing the same http client object in each new requesthere , my singleton class is thread safe too.
private static readonly Lazy<HttpService> lazy =
new Lazy<HttpService>(() => new HttpService());
public static HttpService Instance { get { return lazy.Value; } }
private HttpClient getConnection()
{
client = new HttpClient();
client.Timeout = System.TimeSpan.FromMilliseconds(timeout);
//client.MaxResponseContentBufferSize = 500000;
client.BaseAddress = new Uri(baseAddress);
return client;
}
发布请求代码
public Task<HttpResponseMessage> sendData(String url,String jsonData)
{
var jsonContent = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");
return getConnection().PostAsync(new Uri(url, UriKind.Relative), jsonContent);
}
客户端将使用以下代码执行
client will use following code to execute
HttpService.Instance.sendData(...)
我通过网络浏览了许多库,如 RestSharp
只是为了探索最好的,我发现它们中的大多数都在为每个请求创建新对象.所以我很困惑哪种模式最适合.
i have gone through many libraries like RestSharp
over web just to explore the best and i found that most of them are creating new objects per request. so i am confused which pattern fits best.
推荐答案
更新:似乎使用 HttpClient
的单个静态实例 不尊重DNS变化,所以解决方案是使用HttpClientFactory代码>.请参阅 此处 了解有关它的 Microsoft 文档.
Update: It seems that using a single static instance of HttpClient
doesn't respect DNS changes, so the solution is to use HttpClientFactory
. See here for Microsoft docs about it.
要使用 HttpClientFactory
你必须使用微软的依赖注入.这是 ASP.NET Core 项目的默认设置,但对于其他项目,您必须参考 Microsoft.Extensions.Http 和 Microsoft.Extensions.DependencyInjection.
To use the HttpClientFactory
you have to use Microsoft's dependency injection. This is the default for ASP.NET Core projects, but for others you will have to reference Microsoft.Extensions.Http and Microsoft.Extensions.DependencyInjection.
然后当你创建你的服务容器时,你只需调用 AddHttpClient()
:
Then when you're creating your service container, you simply call AddHttpClient()
:
var services = new ServiceCollection();
services.AddHttpClient()
var serviceProvider = services.BuildServiceProvider();
然后你可以将 IHttpClientFactory
注入你的服务,在幕后 HttpClientFactory
将维护一个 HttpClientHandler
对象池 - 保持你的 DNS连接池耗尽的新鲜和预防问题.
And then you can inject IHttpClientFactory
into your services, and behind the scenes HttpClientFactory
will maintain a pool of HttpClientHandler
objects - keeping your DNS fresh and preventing problems with connection pool exhaustion.
旧答案:
Singleton 是使用 HttpClient
的正确方法.请参阅这篇文章了解完整详情.
Singleton is the correct way to use HttpClient
. Please see this article for full details.
微软 文档状态:
HttpClient 旨在实例化一次并在应用程序的整个生命周期中重复使用.为每个请求实例化一个 HttpClient 类将耗尽重负载下可用的套接字数量.这将导致 SocketException 错误.下面是一个正确使用 HttpClient 的例子.
事实上,我们在我们的应用程序中发现了这一点.我们的代码可能会在 foreach
循环中发出数百个 API 请求,并且对于每次迭代,我们都创建了一个包裹在 using
中的 HttpClient
.我们很快就开始收到来自 MongoClient
的红鲱鱼错误,说它在尝试连接到数据库时超时.阅读链接的文章后,我们发现即使处理了HttpClient
,并且意识到我们正在耗尽可用的套接字.
And indeed, we found this in our application. We have code that can potentially make hundreds of API requests in a foreach
loop, and for each iteration we were creating an HttpClient
wrapped in a using
. We soon started getting red herring errors from our MongoClient
saying that it had timed out trying to connect to the database. After reading the linked article, we found that even after disposing of HttpClient
, and realised that we were exhausting the available sockets.
唯一需要注意的是,DefaultRequestHeaders
和 BaseAddress
之类的东西将应用于使用 HttpClient 的任何地方.作为单例,这可能贯穿整个应用程序.您仍然可以在应用程序中创建多个 HttpClient
实例,但请注意,每次创建时,它们都会创建一个新的连接池,因此应谨慎创建.
The only thing to note is that things like DefaultRequestHeaders
and BaseAddress
will be applied anywhere that HttpClient is used. As a singleton, this is potentially throughout the application. You can still create multiple HttpClient
instances in your application, but just be aware that each time you do, they create a new connection pool and, as such, should be created sparingly.
正如 hvaughan3 所指出的,您也无法更改 HttpClient 使用的 HttpMessageHandler
实例,因此如果这对您很重要,您需要使用带有该处理程序的单独实例.
As pointed out by hvaughan3, you also can't change the instance of HttpMessageHandler
used by the HttpClient, so if this matters to you, you would need to use a separate instance with that handler.
这篇关于单例 httpclient 与创建新的 httpclient 请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!