Skip to content

Commit 04608fd

Browse files
authored
Improve HttpClient resilience benchmarks (#4100)
1 parent 14917b8 commit 04608fd

File tree

3 files changed

+84
-5
lines changed

3 files changed

+84
-5
lines changed

bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/Benchmark.cs renamed to bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HedgingBenchmark.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Net.Http;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -9,11 +10,12 @@
910

1011
namespace Microsoft.Extensions.Http.Resilience.Bench;
1112

12-
public class Benchmark
13+
public class HedgingBenchmark
1314
{
14-
private static HttpRequestMessage Request => new(HttpMethod.Post, "https://bogus");
15+
private static readonly Uri _uri = new("https://bogus");
16+
private static HttpRequestMessage Request => new(HttpMethod.Post, _uri);
1517

16-
private System.Net.Http.HttpClient _client = null!;
18+
private HttpClient _client = null!;
1719

1820
[GlobalSetup]
1921
public void GlobalSetup()

bench/Libraries/Microsoft.Extensions.Http.Resilience.PerformanceTests/HttpClientFactory.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
using System;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Linq;
7+
using System.Threading;
78
using Microsoft.Extensions.Compliance.Redaction;
89
using Microsoft.Extensions.Compliance.Testing;
910
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.DependencyInjection.Extensions;
12+
using Microsoft.Extensions.Logging;
13+
using Microsoft.Extensions.Logging.Abstractions;
1014
using Microsoft.Extensions.Telemetry.Metering;
1115

1216
#pragma warning disable R9A033 // Replace uses of 'Enum.GetName' and 'Enum.ToString' with the '[EnumStrings]' code generator for improved performance
@@ -24,6 +28,10 @@ public enum HedgingClientType
2428

2529
internal static class HttpClientFactory
2630
{
31+
internal const string EmptyClient = "Empty";
32+
33+
internal const string StandardClient = "Standard";
34+
2735
private const string HedgingEndpoint1 = "http://localhost1";
2836
private const string HedgingEndpoint2 = "http://localhost2";
2937

@@ -34,14 +42,25 @@ public static ServiceProvider InitializeServiceProvider(HedgingClientType client
3442
.RegisterMetering()
3543
.AddSingleton<IRedactorProvider>(NullRedactorProvider.Instance)
3644
.AddTransient<NoRemoteCallHandler>()
37-
.AddHedging(clientType);
45+
.AddHedging(clientType)
46+
.AddHttpClient(StandardClient, client => client.Timeout = Timeout.InfiniteTimeSpan)
47+
.AddStandardResilienceHandler()
48+
.Services
49+
.AddHttpClient(StandardClient)
50+
.AddHttpMessageHandler<NoRemoteCallHandler>()
51+
.Services
52+
.AddHttpClient(EmptyClient, client => client.Timeout = Timeout.InfiniteTimeSpan)
53+
.AddHttpMessageHandler<NoRemoteCallHandler>();
54+
55+
services.RemoveAll<ILoggerFactory>();
56+
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
3857

3958
return services.BuildServiceProvider();
4059
}
4160

4261
private static IServiceCollection AddHedging(this IServiceCollection services, HedgingClientType clientType)
4362
{
44-
var clientBuilder = services.AddHttpClient(clientType.ToString());
63+
var clientBuilder = services.AddHttpClient(clientType.ToString(), client => client.Timeout = Timeout.InfiniteTimeSpan);
4564
var hedgingBuilder = clientBuilder.AddStandardHedgingHandler().SelectPipelineByAuthority(SimpleClassifications.PublicData);
4665
_ = clientBuilder.AddHttpMessageHandler<NoRemoteCallHandler>();
4766

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Net.Http;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using BenchmarkDotNet.Attributes;
9+
using Microsoft.Extensions.DependencyInjection;
10+
11+
namespace Microsoft.Extensions.Http.Resilience.Bench;
12+
13+
public class HttpResilienceBenchmark
14+
{
15+
private static readonly Uri _uri = new("https://bogus");
16+
17+
private HttpClient _client = null!;
18+
private HttpClient _standardClient = null!;
19+
private HttpClient _hedgingClient = null!;
20+
21+
private static HttpRequestMessage Request
22+
{
23+
get
24+
{
25+
var request = new HttpRequestMessage(HttpMethod.Post, _uri);
26+
request.Options.Set(new HttpRequestOptionsKey<string>("dummy"), "dummy");
27+
return request;
28+
}
29+
}
30+
31+
[GlobalSetup]
32+
public void GlobalSetup()
33+
{
34+
var serviceProvider = HttpClientFactory.InitializeServiceProvider(HedgingClientType.Ordered);
35+
var factory = serviceProvider.GetRequiredService<IHttpClientFactory>();
36+
_client = factory.CreateClient(HttpClientFactory.EmptyClient);
37+
_standardClient = factory.CreateClient(HttpClientFactory.StandardClient);
38+
_hedgingClient = factory.CreateClient(nameof(HedgingClientType.Ordered));
39+
}
40+
41+
[Benchmark(Baseline = true)]
42+
public Task<HttpResponseMessage> DefaultClient()
43+
{
44+
return _client.SendAsync(Request, CancellationToken.None);
45+
}
46+
47+
[Benchmark]
48+
public Task<HttpResponseMessage> StandardResilienceHandler()
49+
{
50+
return _standardClient.SendAsync(Request, CancellationToken.None);
51+
}
52+
53+
[Benchmark]
54+
public Task<HttpResponseMessage> StandardHedgingHandler()
55+
{
56+
return _hedgingClient.SendAsync(Request, CancellationToken.None);
57+
}
58+
}

0 commit comments

Comments
 (0)