Preface: I am a beginner C# developer and the conclusions below are based off of my understanding of the technology. There may be better ways to do things mentioned on this blog. I may have egregious assumptions or inaccurate conclusions. If so, please feel free to comment.
Azure Functions in the Consumption plan take advantage of the Azure Web App Sandbox. The Sandbox platform enables the auto-scaling of Azure app services including Function apps. A Sandbox is a specialized VM which takes less than 1 second to instantiate. Very cool stuff.
The Azure Web App Sandbox platform has its limitations. One of those being a hard limit on connections in an instance of an app. This article provides more detail.
What does this mean for our RESTful Azure Function App? Well, mainly, that if we do not use the HttpClient library properly, we will run into some serious issues. The HttpClient was designed to be re-usable and the members within the class are thread safe.
In our case, we are using the SendAsAsync method. This method is thread safe meaning that when placed within a 'using' block, the object is disposed of properly once out of scope. This allows us to send Http calls using a static client many times within our Azure Function and the calls will not interfere with eachother when changing URI's, Headers, or methods.
To test this, we created an Azure Function which implements HttpClient poorly.
public callResults CallCheckApi()
using (var client = new HttpClient())
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
var requestContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = client.PostAsync("https://my-api-url.com/api/GetSomething", requestContent);
In the above code, a new client will be instantiated with every call. This may be okay for limited use APIs but in our case, this API is being used by a mobile app and the assumption is that thousands of users may be making calls at any given moment.
Using Jmeter we can observe the bad design. We will setup Jmeter to make 500 calls to the API over 60 seconds. This will surely exhaust the socket limit on the app service as the limit is 300 connections and their default lifetime is 240 seconds. Here is a link to a walkthrough on using Jmeter to test a REST API.
This will vary depending on the API. For instance, I am sending a JSON body so I was required to set the headers using the "Http Header Manager" in Jmeter.
We can see in our Jmeter logs that the API begins returning 500 errors when the server sockets are exhausted. This would be a very bad experience in production. In our case, it would break the process on the mobile app returning bad information to the end user and creating bad data in our backend databases.
To implement HttpClient the right way, based on the articles listed at the beginning of this article, we know that HttpClient is meant to be re-used. To accomplish this, we can initialize the client above our function which creates a static "singleton". We then create a HttpRequestMessage in a 'using' statement. We can place any disposable object in a 'using' statement to properly dispose of once out of scope.
private static HttpClient _httpClient = new HttpClient();
public async Task<callResults> CallCheckApi()
string MyJson = JsonConvert.SerializeObject(c);
using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://my-api-url.com/api/GetSomething/")))
request.Content = new StringContent(MyJson, Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
This example works because the HttpRequest is a member of this instance of the function. If the function gets called 1000 times, there will be 1000 instances of the 'request' all in their own thread. If we decide to make subsequent calls in this code re-using the _httpClient and building new HttpRequestMessages, they will be thread safe!
Enjoy and please feel free to correct me or provide additional information. Thank you to the community for help and information on my learning path.