ASP.NET MVC中的HttpClient Singleton实现

Jos*_*osh 6 c# asp.net-mvc static async-await dotnet-httpclient

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

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

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

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

对于单元测试,我们为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<HttpResponseMessage> SendAsync(HttpRequestMessage request, String userOrProcessName)
        {
            IUnityContainer container = UnityCommon.GetContainer();
            ILogService logService = container.Resolve<ILogService>();

            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
    } 
Run Code Online (Sandbox Code Playgroud)

这是一个叫做的服务:

public class EnterpriseApiService : IEnterpriseApiService
    {
        private static IHttpClient _client;

        static EnterpriseApiService()
        {
            IUnityContainer container = UnityCommon.GetContainer();
            IApplicationSettingService appSettingService = container.Resolve<IApplicationSettingService>();

            _client = container.Resolve<IHttpClient>();
        }

        public EnterpriseApiService() { }

        public Task<HttpResponseMessage> 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

我的问题:

  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属性.

Joh*_* Wu 3

您真的想要一个实例吗?

我认为您不希望在应用程序范围内使用一个实例。您需要每个线程一个实例。否则你不会得到很好的表现!另外,这将解决您的问题 #3 和 #4,因为没有两个线程会同时访问同一个 HttpClient。

你不需要单例

只需将Container.ResolvePerThreadLifetimeManager一起使用即可。

  • 这是一个很好的问题。我找到的文章给出了使用 ConsoleApplication 的示例,并将它们设置为应用程序范围的静态。我不确定这是否适合网络应用程序。 (2认同)