HttpClientHandler / HttpClient Memory Leak

后端 未结 4 1419
逝去的感伤
逝去的感伤 2020-12-03 01:24

I have anywhere from 10-150 long living class objects that call methods performing simple HTTPS API calls using HttpClient. Example of a PUT call:



        
4条回答
  •  萌比男神i
    2020-12-03 02:09

    Here is a basic Api Client that uses the HttpClient and HttpClientHandler efficiently. Do NOT recreate HTTPClient for each request. Reuse Httpclient as much as possible

    My Performance Api Client

    using System;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    using System.Threading.Tasks;
    //You need to install package Newtonsoft.Json > https://www.nuget.org/packages/Newtonsoft.Json/
    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    
    namespace MyApiClient 
    {
        public class MyApiClient : IDisposable
        {
            private readonly TimeSpan _timeout;
            private HttpClient _httpClient;
            private HttpClientHandler _httpClientHandler;
            private readonly string _baseUrl;
            private const string ClientUserAgent = "my-api-client-v1";
            private const string MediaTypeJson = "application/json";
    
            public MyApiClient(string baseUrl, TimeSpan? timeout = null)
            {
                _baseUrl = NormalizeBaseUrl(baseUrl);
                _timeout = timeout ?? TimeSpan.FromSeconds(90);
            }
    
            public async Task PostAsync(string url, object input)
            {
                EnsureHttpClientCreated();
    
                using (var requestContent = new StringContent(ConvertToJsonString(input), Encoding.UTF8, MediaTypeJson))
                {
                    using (var response = await _httpClient.PostAsync(url, requestContent))
                    {
                        response.EnsureSuccessStatusCode();
                        return await response.Content.ReadAsStringAsync();
                    }
                }
            }
    
            public async Task PostAsync(string url, object input) where TResult : class, new()
            {
                var strResponse = await PostAsync(url, input);
    
                return JsonConvert.DeserializeObject(strResponse, new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                });
            }
    
            public async Task GetAsync(string url) where TResult : class, new()
            {
                var strResponse = await GetAsync(url);
    
                return JsonConvert.DeserializeObject(strResponse, new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                });
            }
    
            public async Task GetAsync(string url)
            {
                EnsureHttpClientCreated();
    
                using (var response = await _httpClient.GetAsync(url))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
    
            public async Task PutAsync(string url, object input)
            {
                return await PutAsync(url, new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, MediaTypeJson));
            }
    
            public async Task PutAsync(string url, HttpContent content)
            {
                EnsureHttpClientCreated();
    
                using (var response = await _httpClient.PutAsync(url, content))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
    
            public async Task DeleteAsync(string url)
            {
                EnsureHttpClientCreated();
    
                using (var response = await _httpClient.DeleteAsync(url))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
    
            public void Dispose()
            {
                _httpClientHandler?.Dispose();
                _httpClient?.Dispose();
            }
    
            private void CreateHttpClient()
            {
                _httpClientHandler = new HttpClientHandler
                {
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
                };
    
                _httpClient = new HttpClient(_httpClientHandler, false)
                {
                    Timeout = _timeout
                };
    
                _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
    
                if (!string.IsNullOrWhiteSpace(_baseUrl))
                {
                    _httpClient.BaseAddress = new Uri(_baseUrl);
                }
    
                _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
            }
    
            private void EnsureHttpClientCreated()
            {
                if (_httpClient == null)
                {
                    CreateHttpClient();
                }
            }
    
            private static string ConvertToJsonString(object obj)
            {
                if (obj == null)
                {
                    return string.Empty;
                }
    
                return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                });
            }
    
            private static string NormalizeBaseUrl(string url)
            {
                return url.EndsWith("/") ? url : url + "/";
            }
        }
    }
    

    The usage;

    using ( var client = new MyApiClient("http://localhost:8080"))
    {
        var response = client.GetAsync("api/users/findByUsername?username=alper").Result;
        var userResponse = client.GetAsync("api/users/findByUsername?username=alper").Result;
    }
    

    Note: If you are using a dependency injection library, please register MyApiClient as singleton. It's stateless and safe to reuse the same object for concrete requests.

提交回复
热议问题