HttpClient query occasionally hangs - c#

I initialise HttpClient like so:
public static CookieContainer cookieContainer = new CookieContainer();
public static HttpClient httpClient = new HttpClient(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, CookieContainer = cookieContainer }) { Timeout = TimeSpan.FromSeconds(120) };
so all queries should throw TaskCanceledException if no response is received within 120 seconds.
But some queries (like 1 of 100 000-1 000 000) hang infinitely.
I wrote following code:
public static async Task<HttpResponse> DownloadAsync2(HttpRequestMessage httpRequestMessage)
{
HttpResponse response = new HttpResponse { Success = false, StatusCode = (int)HttpStatusCode.RequestTimeout, Response = "Timeout????????" };
Task task;
if (await Task.WhenAny(
task = Task.Run(async () =>
{
try
{
HttpResponseMessage r = await Global.httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false);
response = new HttpResponse { Success = true, StatusCode = (int)r.StatusCode, Response = await r.Content.ReadAsStringAsync().ConfigureAwait(false) };
}
catch (TaskCanceledException)
{
response = new HttpResponse { Success = false, StatusCode = (int)HttpStatusCode.RequestTimeout, Response = "Timeout" };
}
catch (Exception ex)
{
response = new HttpResponse { Success = false, StatusCode = -1, Response = ex.Message + ": " + ex.InnerException };
}
}),
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(150)).ConfigureAwait(false);
})
).ConfigureAwait(false) != task)
{
Log("150 seconds passed");
}
return response;
}
which actually occasionally executes Log("150 seconds passed");.
I call it like so:
HttpResponse r = await DownloadAsync2(new HttpRequestMessage
{
RequestUri = new Uri("https://address.com"),
Method = HttpMethod.Get
}).ConfigureAwait(false);
Why TaskCanceledException sometimes isn't thrown after 120 seconds?

I don't know with how frequency you call DownloadAsync2, but your code smells a lot for bursting and starving ThreadPool.
Initial number of threads in ThreadPool by default is limited to number of CPU logical cores (usually 12 for today normal systems) and in case of unavailability of threads in ThreadPool, 500ms takes for each new thread to be generated.
So for example:
for (int i = 0; i < 1000; i++)
{
HttpResponse r = await DownloadAsync2(new HttpRequestMessage
{
RequestUri = new Uri("https://address.com"),
Method = HttpMethod.Get
}).ConfigureAwait(false);
}
This code with a high chance will be freezed, specially if you have some lock or any cpu intensive tasks somewhere in your code. Because you invoke new thread per calling DownloadAsync2 so all threads of ThreadPool consumed and many more of them still needed.
I know maybe you say "all of my tasks have been awaited and they release for other works". but they also consumed for starting new DownloadAsync2 threads and you will reach the point that after finishing await Global.httpClient.SendAsync no thread remains for re-assigning and completing the task.
So method have to wait until one thread being available or generated to complete (even after timeout). Rare but feasible.

On Windows & .NET, the number of concurrent outgoing HTTP request to the same endpoint is limited to 2 (as per HTTP 1.1 specification). If you create a ton of concurrent requests to the same endpoint they will queue up. That is one possible explanation to what you experience.
Another possible explanation is this: you don't set the Timeout property of HttpClient explicitly, so it defaults to 100 seconds. If you keep making new requests, while the previous ones didn't finish, system resources will become used up.
I suggest setting the Timeout property to a low value - something proportional to the frequency of the calls you make (1 sec?) and optionally increasing the number of conncurrent outgoing connections with ServicePointManager.DefaultConnectionLimit

I discovered it was httpClient.SendAsync method which occasionally hangs. Therefore I added a cancellation token set to X seconds. But even with a cancelletion token it may sometimes remain stuck and never throw TaskCanceledException.
Therefore I proceeded to workaround that keeps the SendAsync task forever stuck on background and continue with other work.
Here is my workaround:
public static async Task<Response> DownloadAsync3(HttpRequestMessage httpRequestMessage, string caller)
{
Response response;
try
{
using CancellationTokenSource timeoutCTS = new CancellationTokenSource(httpTimeoutSec * 1000);
using HttpResponseMessage r = await Global.httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead, timeoutCTS.Token).WithCancellation(timeoutCTS.Token).ConfigureAwait(false);
response = new Response { Success = true, StatusCode = (int)r.StatusCode, Message = await r.Content.ReadAsStringAsync().ConfigureAwait(false) };
}
catch (TaskCanceledException)
{
response = new Response { Success = false, StatusCode = (int)HttpStatusCode.RequestTimeout, Message = "Timeout" };
}
catch (Exception ex)
{
response = new Response { Success = false, StatusCode = -1, Message = ex.Message + ": " + ex.InnerException };
}
httpRequestMessage.Dispose();
return response;
}
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted
? task
: task.ContinueWith(
completedTask => completedTask.GetAwaiter().GetResult(),
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}

With Flurl, you can configure Timeout per client, per request or globally.
// call once at application startup
FlurlHttp.Configure(settings => settings.Timeout = TimeSpan.FromSeconds(120));
string url = "https://address.com";
// high level scenario
var response = await url.GetAsync();
// low level scenario
await url.SendAsync(
HttpMethod.Get, // Example
httpContent, // optional
cancellationToken, // optional
HttpCompletionOption.ResponseHeaderRead); // optional
// Timeout at request level
await url
.WithTimeout(TimeSpan.FromSeconds(120))
.GetAsync();
Fluent HTTP documentation
Flurl configuration documentation

And the answer is:
ThreadPool.SetMinThreads(MAX_THREAD_COUNT, MAX_THREAD_COUNT);
where MAX_THREAD_COUNT is some number (I use 200). You MUST set at least the second parameter (completionPortThreads), and most probably the first (workerThreads). I had already set the first, but not the second, and now that it is working I am keeping both set.
Alas, this isn't the answer. See comments below

OK, I officially give up! I'm replacing the code with:
try
{
return Task.Run(() => httpClient.SendAsync(requestMessage)).Result;
}
catch (AggregateException e)
{
if (e.InnerException != null)
throw e.InnerException;
throw;
}

Related

c# a task was canceled at the await call

I'm trying to hit my simple server's endpoint using the following code, but I keep getting "A task was canceled." during the await call. The server's logs don't show any errors and cts.IsCancellationRequested == false, however e.CancellationToken.IsCancellationRequested == true. Any advice on how to track down the cause of this cancellation? At the very least, how can I tell if it's coming from the front end or the server endpoint?
private async Task<string> SendSingleRequestToDlis(
HttpClient client,
StringContent requestData)
{
int timeout = 600000; // in ms
string dlisEndpoint = "myendpointhere";
string response;
using (var cts = new CancellationTokenSource(timeout))
{
//send
HttpResponseMessage request;
try
{
request = await client.PostAsync(dlisEndpoint, requestData);
}
catch (Exception e)
{
throw new Exception("Could not establish conection to model hosted on DLIS.", e);
}
....
You are not using your CancellationToken so don't implement it. Just use this:
string dlisEndpoint = "myendpointhere";
string response;
HttpResponseMessage request;
try
{
request = await client.PostAsync(dlisEndpoint, requestData);
}
catch (Exception e)
{
throw new Exception("Could not establish conection to model hosted on DLIS.", e);
}
If you wanted to use the cancellation token you actually have to pass it down into the client.
request = await client.PostAsync(dlisEndpoint, requestData, cts.Source);
At the very least, how can I tell if it's coming from the front end or the server endpoint?
You can know it's at the client because you're seeing an OperationCanceledException. A server can just return a response code, which you would see as an HttpRequestException.
i timed it just now and the cancellation happens at exactly 100s
This is the default value of the HttpClient.Timeout property. Set this property higher (or to Timeout.InfiniteTimeSpan) at the point where the HttpClient is created (or configured, if the HttpClient is injected).

Problem with deadlock waiting for REST call to finish while sending several requests concurrently

I have the following situation. I have a class that iterates through a collection which contains a list of requests that need to be serviced by some REST API. The responses are then processed by another class and returned. The top class will wait for all the requests to finish processing before continuing execution.
Top class
var results = new BlockingCollection<string>();
Task.WaitAll(requests.Select(request => Task.Run(() =>
{
var payload = new MyRequest()
{
ParameterOne = request.FieldOne,
...
};
var partialResult = this.RequestProcessingUtility.Service(payload);
results.Add(partialResult.Result);
})).ToArray());
// continues...
RequestProcessingUtility
public Result Convert(MyRequest request)
{
var result = new Result();
var response = this.RestUtility.Execute<Response, Request>(uri, HttpMethod.Get, request);
// processes response into result
return result;
}
RetsUtility
public RestResult<TResult> Execute<TResult, TRequest>(string uri, HttpMethod method, TRequest request)
where TResult : class
{
HttpResponseMessage response = null;
var message = this.CreateRequest(uri, method, request);
try
{
Task.Run(() =>
{
try
{
response = HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead).Result;
}
catch (Exception exception)
{
throw exception;
}
})
.Wait();
}
catch (AggregateException taskException)
{
// ... something
}
return this.ProcessResult<TResult>(response); // parses HTTP Response
}
If I execute the RestUtility call to this API endpoint synchronously elsewhere in the code, it responds just fine, and quickly to boot. But invoking it in this block deadlocks the execution.
I know I'm messing up something with handling asynchronous calls here, but I can't figure out what.
Thanks.

HttpClient in using statement causes Task cancelled

I created a FileResult : IHttpActionResult webapi return type for my api calls. The FileResult downloads a file from another url and then returns the stream to the client.
Initially my code had a using statement like below:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
using (var httpClient = new HttpClient())
{
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
}
return response;
}
catch (WebException exception)
{...}
}
However this would intermittently cause a TaskCanceledException. I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
Why does that task get canceled? It is not because of a timeout since this has happened on the smallest requests and doesn't always occur on large requests.
When I removed the using statement the code worked properly:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
var httpClient = new HttpClient();
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
return response;
}
catch (WebException exception)
{...}
}
Any idea why the using caused the issue?
I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
But what does that task do? It gets the stream. So, your code ends up with a Stream that may or may not be completely read when it closes the HttpClient.
HttpClient is specifically designed for reuse (and simultaneous use), so I recommend removing the using completely and moving the HttpClient declaration to a static class member. But if you want to close and reopen the clients, you should be able to get it working by reading the stream entirely into memory before closing the HttpClient.
I had a similar issue with Task Canceled exceptions. If you try catching AggregateException or having a catch all Exception block underneath your WebException, you may well find that you catch it, with one exception with the entry stating "A task was canceled"
I did some investigation and found that the AggregateException is quite misleading as described in various threads;
Setting HttpClient to a too short timeout crashes process
How can I tell when HttpClient has timed out?
Bug in httpclientgetasync should throw webexception not taskcanceledexception
I ended up changing my code to set an explicit timeout (where asyncTimeoutInMins is read from the app.config file);
string jsonResponse = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0);
HttpResponseMessage response;
response = await httpClient.GetAsync("/myservice/resource");
// Check the response StatusCode
if (response.IsSuccessStatusCode)
{
// Read the content of the response into a string
jsonResponse = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == HttpStatusCode.Forbidden)
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.Unauthorised);
}
else
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.ApplicationError);
}
}
}
catch (HttpRequestException reqEx)
{
Logger.Instance.Error(reqEx);
Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message);
Environment.Exit((int)ExitCodes.ApplicationError);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
throw;
}
return jsonResponse;

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?
I agree with the accepted answer in that the key to speeding things up is to run the requests in parallel. But any solution that forces additional threads into the mix by use of Task.Run or Parallel.ForEach is not gaining you any efficiency with I/O bound asynchronous operations. If anything it's hurting.
You can easily get all calls running concurrently while letting the underlying async subsystems decide how many threads are required to complete the tasks as efficiently as possible. Chances are that number is much smaller than the number of concurrent calls, because they don't require any thread at all while they're awaiting a response.
Further, the accepted answer creates a new instance of HttpClient for each call. Don't do that either - bad things can happen.
Here's a modified version of the accepted answer:
var httpClient = new HttpClient {
Timeout = TimeSpan.FromMilliseconds(5)
};
var taskList = new List<Task<JObject>>();
foreach (var myRequest in RequestsBatch)
{
// by virtue of not awaiting each call, you've already acheived parallelism
taskList.Add(GetResponseAsync(endPoint, myRequest));
}
try
{
// asynchronously wait until all tasks are complete
await Task.WhenAll(taskList.ToArray());
}
catch (Exception ex)
{
}
async Task<JObject> GetResponseAsync(string endPoint, string myRequest)
{
// no Task.Run here!
var response = await httpClient.PostAsJsonAsync<string>(
string.Format("{0}api/GetResponse", endpoint),
myRequest);
return await response.Content.ReadAsAsync<JObject>();
}
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>();
});
}

Http POST using Task.FromAsync with async request and async response

I have to send an async POST to a web service and I want to send/retrieve the data asynchronously.
I also need to send the request but only wait a maximum of 1500 ms waiting for a response. If I don't get a response, the program should continue on (this is a service making an external web service call). I want to offload these service calls to IOCP's instead of blocking for a long time and waiting for them to return. I only want to block for 1500 ms total.
Here's what I have so far:
var httpRequest = (HttpWebRequest)WebRequest.Create(#"urltoPostTo");
httpRequest.Method = "POST";
byte[] data = Encoding.ASCII.GetBytes("test-post");
httpRequest.ContentLength = data.Length;
var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
.ContinueWith(response =>
{
var localStream = response.Result;
if (localStream != null)
{
localStream.Write(data, 0, data.Length);
localStream.Close();
}
}); //how do I do the continuation for BeginGetResponse and EndGetResponse from here?
I have a couple of requirements that unfortunately I can't change.
I am using Visual Studio 2010 targeting 4.0
I cannot use the Async BCL
I would like to try and use Task.FromAsync
This has already been answered in Implementing extension method WebRequest.GetResponseAsync with support for CancellationToken with a GetResponseAsync extension method that properly handles timeouts and CancellationTokens. When Request.Timeout expires, the method calls Request.Abort before returning the Task in its cancelled state.
The reason for such involved coding is that it's the client's (yours) responsibility to properly handle timeouts, so you can't depend on FromAsync to handle the timeout expiration. Perhaps this is the reason why FromAsync doesn't accept a cancellation token.
Another option is to avoid cancelling the request itself and cancel the continuation. You can use the ContinueWith overload that accepts a CancellationToken and call CancellationTokenSource.CancelAfter to set the cancellation timeout.
This would allow your code to ignore the results and keep running but it wouldn't break the connection to the server and wouldn't stop the background thread from processing any potentially expensive results.
You could write something like this:
var tcs=new CancellationTokenSource();
var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
.ContinueWith(response =>
{...},
cts.Token);
cts.CancelAfter(1500);
Note that the call to CancelAfter is done after starting the asynchronous task.
I would prefer Reed Copsey's extension method in a busy site because a high number of cancelled but outstanding requests can easily exhaust the thread pool, consume a lot of memory without a reason and consume potentially expensive connections to external systems.
why not to work with HttpClient?
webApiHttpClient.PostAsJsonAsync(GetFullAPI("api/Subscribe"), obj)
.ContinueWith(res => _logger.InfoFormat("Subscribe result: {0}", res.Result.StatusCode));
Try this method helper.
public static Task<string> Post(string url, Encoding encoding, string content)
{
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = "POST";
byte[] data = encoding.GetBytes(content);
httpRequest.ContentLength = data.Length;
TaskCompletionSource<string> result = new TaskCompletionSource<string>();
Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
.ContinueWith(requestStreamTask =>
{
try
{
using (var localStream = requestStreamTask.Result)
{
localStream.Write(data, 0, data.Length);
localStream.Flush();
}
Task.Factory.FromAsync<WebResponse>(httpRequest.BeginGetResponse, httpRequest.EndGetResponse, httpRequest)
.ContinueWith(responseTask =>
{
try
{
using (var webResponse = responseTask.Result)
using (var responseStream = webResponse.GetResponseStream())
using (var sr = new StreamReader(responseStream, encoding))
{
result.SetResult(sr.ReadToEnd());
}
}
catch (Exception e)
{
result.SetException(e);
}
}, TaskContinuationOptions.AttachedToParent);
}
catch (Exception e)
{
result.SetException(e);
}
}, TaskContinuationOptions.AttachedToParent);
return result.Task;
}

Categories

Resources