Polly Timeout: How to utilize optimistic timeout on all HttpClient.GetAsync calls when using HttpClientFactory
See original GitHub issueSummary: What are you wanting to achieve?
This is somewhat related to #907, but within the context of HttpClientFactory
. If a CancellationToken
is required to be passed to the HttpClient
instance for optimistic timeout, how would you go about doing that when nearly everything about the policy setup with HttpClientFactory
is implicit? There is no CancellationToken
object in sight to reference or pass around!
What code or approach do you have so far?
I have two separate source files here: the main program where all the DI-framework magic with HttpClientFactory
happens, and the GitHubService
class that actually makes the HTTP calls.
In Program.cs:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var jitterDelay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 6);
var retryPolicy = Policy
.Handle<RateLimitRejectedException>()
.OrTransientHttpError()
.WaitAndRetryAsync(jitterDelay);
var timeoutPolicy = Policy
.TimeoutAsync<HttpResponseMessage>(10);
var host = new HostBuilder()
.ConfigureServices(services =>
{
services.AddHttpClient<IGitHubService, GitHubService>((sp, client) =>
{
client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
client.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
client.Timeout = TimeSpan.FromSeconds(60); // overall timeout
// pass config file as second argument in GitHubService constructor
ActivatorUtilities.CreateInstance<GitHubService>(sp, client, config);
})
.AddPolicyHandler(retryPolicy);
.AddPolicyHandler(timeoutPolicy);
})
.Build();
var gitHubService = host.Services.GetRequiredService<GitHubService>();
var gitHubZen = await gitHubService.GetZenAsync();
Console.WriteLine(gitHubZen);
In GitHubService.cs:
public class GitHubService : IGitHubService
{
private readonly HttpClient _httpClient;
// other private fields to populate via config
public GitHubService(HttpClient httpClient, IConfiguration config)
{
_httpClient = httpClient;
// populate private fields via config
}
public async Task<string> GetZenAsync()
{
using var response = await _httpClient.GetAsync("/zen");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public interface IGitHubService
{
}
Even if I modified GetAsync()
to include a CancellationToken
, where would it come from? Should it be included in the constructor for GitHubService
?
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (4 by maintainers)
Top GitHub Comments
Oof, so that explains why a lot of the documentation regarding
HttpClientFactory
keeps squawking about crap I don’t care about such asPageModel
. My use case is simply just a console app that gets scheduled to run at specific times via Task Manager in Windows Server and needs to communicate to API endpoints via HTTP. I also like the idea of having a well-behaved client app that talks to API endpoints, which is how I stumbled upon Polly andHttpClientFactory
, which solves the exhausted socket and stale DNS issues.Anyway, I didn’t realize until you told me that
CancellationTokenSource.Token
exists. I might just use that for per-call timeouts instead while leaving the other policies in place. I have another use case where the overall timeout in HttpClient is set to 5 minutes and the per-call timeout has to be 1 minute, so I’m guessing that will change the calculus a bit when deciding between pessimistic vs. optimistic.Thanks for the discussion. As I continue to work through this console app, I’ll make note on which parts of the documentation aren’t clear and give feedback (if any) to #853.
Great, thanks!