mspnp / performance-optimization

Guidance on how to observe, measure, and correct common issues in a cloud-based system.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Improper Instantiation Question

schellem opened this issue · comments

Really great stuff here but being able to determine when a class\methods is shareable seems to be 'conflicting'. Example of the HttpClient, talked about in the docs/ImproperInstantiation.md doc. If I go look at the docs for it states the following

'Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.'

The HttpClient.GetStringAsync is not marked as 'public static' so it appears to not be thread safe to me. Am I missing something here?

Great catch. The MSDN docs for HttpClient are in the process of being updated.

On the same topic-
We are creating a wrapper for HttpClient. As we are going to follow performance optimization guidance from https://github.com/mspnp/performance-optimization. We want to avoid anti-pattern - Improper instantiation mentioned in that document. I referred this guidance to my team to use static HttpClient. The feedback I have got is on thread-safety. Each request has a header containing user claim. Since I have a static HttpClient, will it be thread-safe? If we have multiple requests hitting the code (for example GET) at the same time, will it be a race condition to set header? We have implementation as below.

public class HttpClientHelper{
    private static readonly HttpClient _HttpClient;
    static HttpClientHelper() {
            HttpClient = new HttpClient();
            HttpClient.Timeout = TimeSpan.FromMinutes(SOME_CONFIG_VALUE);
    }

    public async Task<HttpResponseMessage> CallHttpClientPostAsync(string requestUri, HttpContent requestBody)
    {
        AddHttpRequestHeader(httpClient);
        var response = await httpClient.PostAsync(requestUri, requestBody); //Potential thread synchronization issue???
        return response;
    }

    public HttpResponseMessage CallHttpClientGet(string requestUri)
    {
        AddHttpRequestHeader(httpClient);
        var response = httpClient.GetAsync(requestUri).Result; //Potential thread synchronization issue???
        return response;
    }

    private void AddHttpRequestHeader(HttpClient client)
    {
        string HeaderName = "CorrelationId";
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Properties.Settings.Default.HttpClientAuthHeaderScheme, GetTokenFromClaims()); //Race condition???
        if (client.DefaultRequestHeaders.Contains(HeaderName))
            client.DefaultRequestHeaders.Remove(HeaderName);
        client.DefaultRequestHeaders.Add(HeaderName, Trace.CorrelationManager.ActivityId.ToString());
    }
}

Please suggest.

As per Carlos suggestion, we will new up HttpRequestMessage object and add header to it to make it thread-safe. That way we will still use static HttpClient but header will be private value per request.

public async Task SendAsync(){
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/api/todolist");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer","securitytokena" );
    HttpResponseMessage response = await HttpClient.SendAsync(request); //this is static httpclient 
}

Yes, that makes a lot of sense. Setting the default headers should just be done once for all requests that will be used by that instance of HttpClient, while headers that are unique to the request should be added with each HttpRequestMessage.