0
votes

I read in an article about HttpClient/DocumentClient that it can be best practice to create a singleton for apps and inject it into object so that underlying resources are not exhausted by continued re-creating.How does this work? If an HttpClient is being accessed by various threads and making simultaneous calls to possibly different endpoints I can't see how this can work.

I read this

   https://medium.com/@nuno.caneco/c-httpclient-should-not-be-disposed-or-should-it-45d2a8f568bc

with interest. If I have an Azure Function making use of a DocumentClient calling cosmosDb how should I use the DocumentClient? Should it be a static instance?

I have my Azure function set up like this. I presume a new instance of DocmentClient is being created with every request which under high load could cause resource problems.

[FunctionName("MyGetFunc")]
    public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    [CosmosDB("ct","ops", ConnectionStringSetting ="cosmosConn")] 
    DocumentClient docClient,
    ILogger log)

    //use docClient here...
    {
4
HttpClient is thread safe. Apart from that, why not call different URLs? HttpClient isn't HttpWebRequest which can only be used for one URL, in fact, just one call. - Panagiotis Kanavos

4 Answers

2
votes

If an HttpClient is being accessed by various threads and making simultaneous calls to possibly different endpoints I can't see how this can work.

Why? The HttpClient is thread-safe which means that it can be used from several concurrent threads simultaneously.

Is HttpClient safe to use concurrently?

2
votes

If you're using .NET Core, please also refer to Use HttpClientFactory to implement resilient HTTP requests.

HttpClient is intended to be instantiated once and reused throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. That issue will result in SocketException errors. Possible approaches to solve that problem are based on the creation of the HttpClient object as singleton or static, as explained in this Microsoft article on HttpClient usage.

But there’s a second issue with HttpClient that you can have when you use it as singleton or static object. In this case, a singleton or static HttpClient doesn't respect DNS changes, as explained in this issue at the .NET Core GitHub repo.

To address those mentioned issues and make the management of HttpClient instances easier, .NET Core 2.1 introduced a new HttpClientFactory that can also be used to implement resilient HTTP calls by integrating Polly with it.

2
votes

For the DocumentClient part of your question see here: https://docs.microsoft.com/en-us/sandbox/functions-recipes/cosmos-db?tabs=csharp#customize-a-documentclient-and-reuse-it-between-executions

They talk about the different scenarios. So yes, if you have many Function invocations, I would use one static instance - which is also thread-safe.

private static DocumentClient client = GetCustomClient();
private static DocumentClient GetCustomClient()
{
    DocumentClient customClient = new DocumentClient(
        new Uri(ConfigurationManager.AppSettings["CosmosDBAccountEndpoint"]), 
        ConfigurationManager.AppSettings["CosmosDBAccountKey"],
        new ConnectionPolicy
        {
            ConnectionMode = ConnectionMode.Direct,
            ConnectionProtocol = Protocol.Tcp,
            // Customize retry options for Throttled requests
            RetryOptions = new RetryOptions()
            {
                MaxRetryAttemptsOnThrottledRequests = 10,
                MaxRetryWaitTimeInSeconds = 30
            }
        });

    // Customize PreferredLocations
    customClient.ConnectionPolicy.PreferredLocations.Add(LocationNames.CentralUS);
    customClient.ConnectionPolicy.PreferredLocations.Add(LocationNames.NorthEurope);

    return customClient;
}

[FunctionName("CosmosDbSample")]
public static async Task<HttpResponseMessage> Run(
0
votes
[CosmosDB("ct","ops", ConnectionStringSetting ="cosmosConn")] 
    DocumentClient docClient,

That is using the Cosmos DB Binding. The Binding does not create multiple instances of the DocumentClient, it will create one and reuse it in all executions.

You can check the source code here: https://github.com/Azure/azure-webjobs-sdk-extensions/blob/dev/src/WebJobs.Extensions.CosmosDB/Bindings/CosmosDBClientBuilder.cs.

It calls GetService and obtains the DocumentClient instance for that particular connection string if one already was created in a previous execution.

Similarly to maintaining your own static/Lazy DocumentClient (see https://docs.microsoft.com/en-us/azure/azure-functions/manage-connections#documentclient-code-example-c).