Performance Issue: .NET HttpClient Response is slow - c#

Problem:
I am using .NET Class HttpClient to make Requests to the endpoint URL.
My Code:
using (HttpClient apiClient1 = new HttpClient())
{
apiClient.GetAsync(apiUrl).Result;
}
Problem Identified:
If I use using block, I'm opening multiple connections, leads to socketExceptions.
Changed My Above Code to:
public class RequestController: ApiController
{
private static HttpClient apiClient = new HttpClient();
[HttpPost]
public dynamic GetWebApiData([FromBody] ParamData params)
{
var resultContent = apiClient.GetAsync(apiUrl).Result.Content;
return Newtonsoft.Json.JsonConvert.DeserializeObject<object>(resultContent.ReadAsStringAsync().Result);
}
}
Result of the above code after making HttpClient as static is as follows:
Only one Connection is established.
For Each request, I'm looking for the 200 milliseconds reduction in
Response Time.
What I Need:
I want to make conurrent calls atleast 50 calls to the end point with High-Speed response.
Kindly help me with this scenario.

Use the async API and stop calling .Result blocking calls.
public class RequestController: ApiController {
private static HttpClient apiClient = new HttpClient();
[HttpPost]
public async Task<IHttpActionResult> GetWebApiData([FromBody] ParamData data) {
var response = await apiClient.GetAsync(apiUrl);
var resultContent = response.Content;
var model = await resultContent.ReadAsAsync<dynamic>();
return Ok(model);
}
}

The default SSL connection idle timeout of HttpClient are 2 mins. So, after that you have to re handshake with the server. This maybe the root cause.
You could follow this article(https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core) to extend the timeout. But the testing result from my side, I could only extend to 5 mins. After that, the connection will be closed.

Related

What is good practice when it comes to HttpClient in C# [duplicate]

This question already has answers here:
Singleton httpclient vs creating new httpclient request
(5 answers)
Closed 17 days ago.
I've been learning C# over the past month and started to learn about HTTP requests using the HttpClient class. Right now I have a basic controller in my MVC project
private readonly HttpClient _client = new HttpClient();
private HttpResponseMessage response = new HttpResponseMessage();
public IActionResult Index() {
return View();
}
[HttpGet("/data")]
async public Task < string > Data(int ? postId) {
if (postId != null) {
response = await _client.GetAsync("https://jsonplaceholder.typicode.com/comments?postId=" + postId);
} else {
response = await _client.GetAsync("https://jsonplaceholder.typicode.com/comments");
}
if (response.IsSuccessStatusCode) {
return response.Content.ReadAsStringAsync().Result;
}
return "There was an error!";
}
This works perfectly fine, except I wanted to know if it's the 'right' way of doing things. I initiated the HttpClient and HttpResponseMessage when the controller class in initialized when the web app starts up, but should I do this inside the GET route? I read somewhere on Microsoft's website it's good to initialize only once per application.
The methods you are using on HttpClient are thread safe, so you are correct in that you should only initialize it once. Even better in a singleton that handles the api requests.
However, HttpResponseMessage should be stored on a per-use basis to avoid nasty race conditions or unintentional side-effects.
In this case race-conditions are not an issue because asp creates a new instance of the controller for every request by default, but keeping values in the smallest scope is good practice to avoid unintentional side-effects by accidentally overwriting the value.
For example:
private async Task<string> CallOtherExternalApi() {
response = await _client.GetAsync("https://example.com");
return await response.Content.ReadAsStringAsync();
}
[HttpGet("/data")]
public async Task <string> Data(int? postId) {
response = await _client.GetAsync("https://jsonplaceholder.typicode.com/comments");
var otherData = await CallOtherExternalApi(); // response got overwritten
// Now this code is checking the status of `CallOtherExternalApi`
if (response.IsSuccessStatusCode) {
return response.Content.ReadAsStringAsync().Result;
}
return "There was an error!";
}

ASP.NET Core synchronously wait for http request

I have an external endpoint which I call to get some Json response.
This endpoint will initiate a session to a POS device, so the device will show the request details and ask the customer to enter his credit card to complete the payment, then when the customer finishes; the POS will call the endpoint and it will return the result back to my application.
The problem here is that I need the operation to complete as described in this scenario (synchronously).
When I do the call to this endpoint from postman; it waits a lot of time (until the POS receives the request and customer do his entries then returns the results back to endpoint and endpoint returns the results back to Postman) ... this is all works fine.
The problem is when I do this from an ASP.NET Core app, the request is not waited for endpoint and the response is returned with null directly.
I need something to wait for it.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("x-API-Key", "ApiKey");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var postTask = client.PostAsJsonAsync(new Uri("terminalEndpoint here"), dto);//dto is the request payload
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
//Should hang after this line to wait for POS
var terminalPaymentResponseDto = result.Content.ReadAsAsync<InitiateTerminalPaymentResponseDto>().Result;
//Should hit this line after customer finishes with POS device
return terminalPaymentResponseDto;
}
}
First of all, there's no need to block. In fact, in an ASP.NET Core application you should avoid blocking as much as possible. Use async and await instead. This allows ASP.NET Core to use the freed threadpool thread for other work.
Second, HttpClient is thread-safe and meant to be reused. Creating a new one every time in a using block leaks sockets. You could use a static instance but a better solution is to use IHttpClientFactory as Make HTTP requests using IHttpClientFactory in ASP.NET Core shows, to both reuse and recycle HttpClient instances automatically.
Finally, there's no reason to add these headers on every call. The Content-Type is set by PostAsJsonAsync anyway. I also suspect the API key doesn't change when calling the same server either.
In your Startup.cs or Program.cs you can use AddHttpClient to configure the API Key :
builder.Services.AddHttpClient(client=>{
client.DefaultRequestHeaders.Add("x-API-Key", "ApiKey");
});
After that you can inject IHttpClientFactory into your controllers or pages and call it asynchronously in asynchronous actions or handlers :
public class MyController:ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
public MyController:ControllerBase(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public async Task<InitiateTerminalPaymentResponseDto> PostAsync(MyDTO dto)
{
var client=_httpClientFactory.CreateClient();
var uri=new Uri("terminalEndpoint here");
var result = client.PostAsJsonAsync(uri, dto);payload
if (result.IsSuccessStatusCode)
{
//Should hang after this line to wait for POS
var paymentDto= await result.Content.ReadAsAsync<InitiateTerminalPaymentResponseDto>();
//Should hit this line after customer finishes with POS device
return paymentDto;
}
else {
//Do whatever is needed in case of error
}
}
}
Using HttpClientFactory allows adding retry strategies using Polly eg, to recover from a temporary network disconnection.
Why not use the await like below? And make sure to change the function to async
var postTask = await client.PostAsJsonAsync(new Uri("terminalEndpoint here"), dto);

Exception while posting to web API, HTTPClient already disposed

I'm posting a bytearray from an Android App in Xamarin.Forms to an .NET Core 2.0 WebAPI. However, I'm getting an exception saying that the NetworkStream already is disposed;
Code making the request;
public async Task PostImageAsync(ImageDTO image)
{
var content = new MultipartFormDataContent();
var byteArrayContent = new ByteArrayContent(image.Content);
content.Add(byteArrayContent, image.FileTile, image.FileName);
try
{
using (var httpClient = GetNewHttpClient())
{
SetBearerToken(httpClient);
var response = await httpClient.PostAsync($"{_apiUrl}/api/images/upload", content);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
catch (Exception e)
{
//Exception occurs here
var msg = e.GetBaseException().Message;
throw;
}
}
Code to get the HttpClient
private HttpClient GetNewHttpClient()
{
//HttpClientHandler is a global variable
var httpClient = new HttpClient(HttpClientHandler, false) {BaseAddress = new Uri(_apiUrl)};
return httpClient;
}
API Endpoint
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0) return BadRequest();
return Ok();
}
EDIT - SetBearerToken Method
private static void SetBearerToken(HttpClient client)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", App.StoredToken);
}
The Exception:
cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'.
It feels like a really obvious mistake I'm making here, but I can't get my head around it. Anybody has any ideas?
Don't dispose objects inside async functions
A using statement in an async method is "odd" in that the Dispose
call may execute in a different thread to the one which acquired the
resource (depending on synchronization context etc) but it will still
happen... assuming the thing you're waiting for ever shows up or
fail, of course. (Just like you won't end up calling Dispose in
non-async code if your using statement contains a call to a method
which never returns.)
#jon-skeet https://stackoverflow.com/a/16566605/2228916
Don’t dispose of the HttpClient:
https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Also noticed that you set _apiUrl as the BaseAddress and prefix the url in the post. Pick one or the other.

Best Practice for Use HttpClient

I'm using HttpClient to make request to WebApi.
I have written this code
public async Task<string> ExecuteGetHttp(string url, Dictionary<string, string> headers = null)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (headers != null)
{
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
Now I'm calling this method from my action.
public async Task<ActionResult> Index()
{
try
{
RestWebRequest RestWebRequest = new RestWebRequest();
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Authorization", "bearer _AxE9GWUO__8iIGS8stK1GrXuCXuz0xJ8Ba_nR1W2AhhOWy9r98e2_YquUmjFsAv1RcI94ROKEbiEjFVGmoiqmUU7qB5_Rjw1Z3FWMtzEc8BeM60WuIuF2fx_Y2FNTE_6XRhXce75MNf4-i0HbygnClzqDdrdG_B0hK6u2H7rtpBFV0BYZIUqFuJpkg4Aus85P8_Rd2KTCC5o6mHPiGxRl_yGFFTTL4_GvSuBQH39RoMqNj94A84KlE0hm99Yk-8jY6AKdxGRoEhtW_Ddow9FKWiViSuetcegzs_YWiPMN6kBFhY401ON_M_aH067ciIu6nZ7TiIkD5GHgndMvF-dYt3nAD95uLaqX6t8MS-WS2E80h7_AuaN5JZMOEOJCUi7z3zWMD2MoSwDtiB644XdmQ5DcJUXy_lli3KKaXgArJzKj85BWTAQ8xGXz3PyVo6W8swRaY5ojfnPUmUibm4A2lkRUvu7mHLGExgZ9rOsW_BbCDJq6LlYHM1BnAQ_W6LAE5P-DxMNZj7PNmEP1LKptr2RWwYt17JPRdN27OcSvZZdam6YMlBW00Dz2T2dgWqv7LvKpVhMpOtjOSdMhDzWEcf6yqr4ldVUszCQrPfjfBBtUdN_5nqcpiWlPx3JTkx438i08Ni8ph3gDQQvl3YL5psDcdwh0-QtNjEAGvBdQCwABvkbUhnIQQo_vwA68ITg07sEYgCl7Sql5IV7bD_x-yrlHyaVNtCn9C4zVr5ALIfj0YCuCyF_l1Z1MTRE7nb");
var getCategories = await RestWebRequest.ExecuteGetHttp("http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true", headers);
}
catch (HttpRequestException ex)
{
return View();
}
return View();
}
Now It is said that HttpClient has been designed to be re-used for multiple calls.
How Can I use same httpClient object for multiple calls.
Let's suppose
First I'm calling
http://localhost:53646/api/Job/GetAllCategories?isIncludeChild=true
Now In same controller I have to call another Api with diffrent header and diffrent url.
http://localhost:53646/api/Job/category/10
Should I make the global object of HttpClient and Use the same object for all API calls.
The challenge in using just one HttpClient across your application is when you want to use different credentials or you try to vary the default headers for your requests (or anything in the HttpClientHandler passed in). In this case you will need a set of purpose specific HttpClients to re-use since using just one will be problematic.
I suggest creating a HttpClient per the "type" of request you wish to make and re-use those. E.g. one for each credential you need - and maybe if you have a few sets of default headers, one per each of those.
It can be a bit of a juggling act between the HttpClient properties (which are not thread safe) and need their own instance if being varied:
- BaseAddress
- DefaultRequestHeaders
- MaxResponseContentBufferSize
- Timeout
And what you can pass in to the "VERB" methods (get, put, post etc). For example, using HttpClient.PostAsync Method (String, HttpContent) you can specify your headers for the [HttpContent][3] (and not have to put them in the HttpClient DefaultHeaders).
All of the Async methods off the HttpClient are thread safe (PostAsync) etc.
Just because you can, doesn't mean you should.
You don't have to, but you can reuse the HttpClient, for example when you want to issue many HTTP requests in a tight loop. This saves a tiny fraction of time it takes to instantiate the object.
Your MVC controller is instantiated for every request. So it won't harm any significant amount of time to instantiate a HttpClient at the same time. Remember you're going to issue an HTTP request with it, which will take many orders more time than the instantiation ever will.
If you do insist you want to reuse one instance, because you have benchmarked it and evaluated the instantiation of HttpClient to be your greatest bottleneck, then you can take a look at dependency injection and inject a single instance into every controller that needs it.
in .net core you can do the same with HttpClientFactory something like this:
public interface IBuyService
{
Task<Buy> GetBuyItems();
}
public class BuyService: IBuyService
{
private readonly HttpClient _httpClient;
public BuyService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Buy> GetBuyItems()
{
var uri = "Uri";
var responseString = await _httpClient.GetStringAsync(uri);
var buy = JsonConvert.DeserializeObject<Buy>(responseString);
return buy;
}
}
ConfigureServices
services.AddHttpClient<IBuyService, BuyService>(client =>
{
client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});
documentation and example at here and here

Unresponsiveness with async event handlers in WPF in .NET 4.5

I have created a simple async operation which is being kicked of when the button is clicked. Here is the whole code:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_1(object sender, RoutedEventArgs e) {
var htmlString = await DowloadPage("http://example.com");
txtBlock1.Text = htmlString;
}
public async Task<string> DowloadPage(string uri) {
using (WebClient client = new WebClient()) {
var htmlString = await client.DownloadStringTaskAsync(uri);
return htmlString;
}
}
}
Very easy. But when I click the button, I experience unresponsiveness on the UI thread. When I try to move around the window while the page is being downloaded, I am unable to.
Any idea what is going wrong?
Edit:
I tried with HttpClient in .NET 4.5 and it worked out pretty great as expected:
public async Task<string> DowloadPage(string uri) {
using (HttpClient client = new HttpClient()) {
var response = await client.GetAsync(uri);
var htmlString = await response.Content.ReadAsStringAsync();
return htmlString;
}
}
WebClient uses HttpWebRequest, which unfortunately is not very asynchronous, even if you use the "asynchronous" methods. It does a blocking DNS lookup, at least. It may also block during proxy negotiation and/or the initial HTTP connection.
An older release of HttpClient was just using a wrapper around HttpWebRequest. I requested a truly-asynchronous HttpClient, but never heard a response. The last time I checked HttpClient, it was still part of MVC; ASP.NET Web API wasn't around at that time, so they may have fixed HttpClient since then. Or the difference in behavior between WebClient and HttpClient on your machine may just have to do with DNS caches or some such.

Categories