ASP.NET MVC中的HttpClient Singleton实现

在www.asp.net上阅读此博客文章和官方说明后 :

HttpClient旨在实例化一次,并在应用程序的整个生命周期中重复使用。 特别是在服务器应用程序中,为每个请求创建一个新的HttpClient实例将耗尽重负载下可用的套接字数量。 这将导致SocketException错误。

我发现我们的代码在每次调用时都处理了HttpClient。 我正在更新我们的代码,以便我们重用HttClient,但我关心的是我们的工具但不是线程安全的。

以下是新代码的当前草案:

对于unit testing,我们为HttpClient实现了一个包装器,消费者调用包装器:

public class HttpClientWrapper : IHttpClient { private readonly HttpClient _client; public Uri BaseAddress { get { return _client.BaseAddress; } set { _client.BaseAddress = value; } } public HttpRequestHeaders DefaultRequestHeaders { get { return _client.DefaultRequestHeaders; } } public HttpClientWrapper() { _client = new HttpClient(); } public Task SendAsync(HttpRequestMessage request, String userOrProcessName) { IUnityContainer container = UnityCommon.GetContainer(); ILogService logService = container.Resolve(); logService.Log(ApplicationLogTypes.Debug, JsonConvert.SerializeObject(request), userOrProcessName); return _client.SendAsync(request); } #region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing && _client != null) { _client.Dispose(); } disposedValue = true; } } public void Dispose() { Dispose(true); } #endregion } 

这是一个调用的服务:

 public class EnterpriseApiService : IEnterpriseApiService { private static IHttpClient _client; static EnterpriseApiService() { IUnityContainer container = UnityCommon.GetContainer(); IApplicationSettingService appSettingService = container.Resolve(); _client = container.Resolve(); } public EnterpriseApiService() { } public Task CallApiAsync(Uri uri, HttpMethod method, HttpContent content, HttpRequestHeaders requestHeaders, bool addJsonMimeAccept = true) { IUnityContainer container = UnityCommon.GetContainer(); HttpRequestMessage request; _client.BaseAddress = new Uri(uri.GetLeftPart(UriPartial.Authority)); if (addJsonMimeAccept) _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request = new HttpRequestMessage(method, uri.AbsoluteUri); // Removed logic that built request with content, requestHeaders and method return _client.SendAsync(request, UserOrProcessName); } } 

我的问题:

  1. 这是重用HttpClient对象的合适方法吗?
  2. 是否为EnterpriseApiService的所有实例共享了静态_httpClient字段(填充了静态构造函数)? 我想确认,因为实例方法正在调用它。
  3. 当调用CallApiAsync()时,当对静态HttpClient进行更改时,例如“_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(”application / json“))”这些值可能会被最后一个进程覆盖行“_client.SendAsync”被调用? 我担心在处理CallApiAsync()的过程中,静态实例会更新。
  4. 由于它调用SendAsync(),我们是否保证响应被映射回正确的调用者? 我想确认响应不会转到另一个调用者。

更新:由于我已经删除了USING语句,并且Garage Collection没有调用Dispose,我将采用更安全的方法在该方法中创建新实例。 要在线程生存期内重用HttpClient实例,它需要对逻辑进行重要的重写,因为该方法为每次调用设置HttpClient属性。

你真的想要一个实例吗?

我不认为你想要一个应用程序范围的实例。 你想要每个线程一个实例。 否则你将无法获得很好的表现! 此外,这将解决您的问题#3和#4,因为没有两个线程将同时访问相同的HttpClient。

你不需要单身人士

只需将Container.Resolve与PerThreadLifetimeManager一起使用即可 。

对于那些幸运地使用.NET Core的人来说,这是相当简单的。

正如John Wu如此雄辩地说,你不需要单身人士,而是每个请求单身。 因此, AddScoped()方法就是你所追求的。

在您的ConfigureServices(IServiceCollection services)方法中:

 services.AddScoped(); 

消费:

 public class HomeController { readonly HttpClient client; public HomeController (HttpClient client) { this.client = client; } //rest of controller code } 

由于它调用SendAsync(),我们是否保证响应被映射回正确的调用者? 我想确认响应不会转到另一个调用者。

这将通过回调指针处理。 它与使用HttpClient作为单例无关。 更多细节 – https://stackoverflow.com/a/42307650/895724