Search code examples
c#timeoutasync-awaitdotnet-httpclient

HttpClient - Send a batch of requests


I want to iterate a batch of requests, sending each one of them to an external API using HttpClient class.

  foreach (var MyRequest in RequestsBatch)
  {
            try
            {
                HttpClient httpClient = new HttpClient();
                httpClient.Timeout = TimeSpan.FromMilliseconds(5);
                HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), myRequest);
                JObject resultResponse = await response.Content.ReadAsAsync<JObject>();
            }
            catch (Exception ex)
            {
                continue;
            }
 }

The context here is I need to set a very small timeout value, so in case the response takes more than that time, we simply get the "Task was cancelled" exception and continue iterating.

Now, in the code above, comment these two lines:

                HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), myRequest);
                resultResponse = await response.Content.ReadAsAsync<JObject>();

The iteration ends very fast. Uncomment them and try again. It takes a lot of time.

I wonder if calling PostAsJsonAsync/ReadAsAsync methods with await takes more time than the timeout value?

Based on the answer below, supposing it will create different threads, we have this method:

  public Task<JObject> GetResponse(string endPoint, JObject request, TimeSpan timeout)
    {
        return Task.Run(async () =>
        {
            try
            {
                HttpClient httpClient = new HttpClient();
                httpClient.Timeout = TimeSpan.FromMilliseconds(5);
                HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endPoint), request).WithTimeout<HttpResponseMessage>(timeout);
                JObject resultResponse = await response.Content.ReadAsAsync<JObject>().WithTimeout<JObject>(timeout);
                return resultResponse;
            }
            catch (Exception ex)
            {
                return new JObject() { new JProperty("ControlledException", "Invalid response. ")};
            }
        });
    }

An exception is raised there and the JObject exception should be returned, very fast, however, if using httpClient methods, even if it raises the exception it takes a lot of time. Is there a behind the scenes processing affecting the Task even if the return value was a simple exception JObject?

If yes, which another approach could be used to send a batch of requests to an API in a very fast way?


Solution

  • It doesn't look like you're actually running a seperate thread for each request. Try something like this:

    var taskList = new List<Task<JObject>>();
    
    foreach (var myRequest in RequestsBatch)
    {
        taskList.Add(GetResponse(endPoint, myRequest));
    }
    
    try
    {
        Task.WaitAll(taskList.ToArray());
    }
    catch (Exception ex)
    {
    }
    
    public Task<JObject> GetResponse(string endPoint, string myRequest)
    {
        return Task.Run(() =>
            {
                HttpClient httpClient = new HttpClient();
    
                HttpResponseMessage response = httpClient.PostAsJsonAsync<string>(
                     string.Format("{0}api/GetResponse", endpoint), 
                     myRequest, 
                     new CancellationTokenSource(TimeSpan.FromMilliseconds(5)).Token);
    
                JObject resultResponse = response.Content.ReadAsAsync<JObject>();
            });
    }