Skip to content

Commit 0045d7d

Browse files
authored
Add more options to perf client (#2648)
1 parent d07efb5 commit 0045d7d

File tree

5 files changed

+81
-43
lines changed

5 files changed

+81
-43
lines changed

perf/benchmarkapps/GrpcClient/ClientOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ public class ClientOptions
3939
public GrpcClientType GrpcClientType { get; set; }
4040
public int Streams { get; set; }
4141
public int Deadline { get; set; }
42+
public bool WinHttpHandler { get; set; }
4243
}

perf/benchmarkapps/GrpcClient/GrpcClient.csproj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net9.0</TargetFramework>
4+
<TargetFramework Condition="'$(NetFxFramework)'!='true'">net9.0</TargetFramework>
5+
<TargetFramework Condition="'$(NetFxFramework)'=='true'">net462</TargetFramework>
56
<OutputType>Exe</OutputType>
67
<DefineConstants Condition="'$(EnableGrpcWeb)' == 'true'">$(DefineConstants);GRPC_WEB</DefineConstants>
78
<!-- Enable server GC to more closely simulate a microservice app making calls -->
@@ -23,6 +24,7 @@
2324
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
2425
<PackageReference Include="Newtonsoft.Json" />
2526
<PackageReference Include="System.CommandLine" />
27+
<PackageReference Include="System.Net.Http.WinHttpHandler" />
2628

2729
<PackageReference Include="Google.Protobuf" />
2830
<PackageReference Include="Grpc.Core" />
@@ -49,9 +51,4 @@
4951
<PackageReference Include="Grpc.Net.Client" />
5052
</ItemGroup>
5153

52-
53-
<ItemGroup Condition="$([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), 5.0))">
54-
55-
</ItemGroup>
56-
5754
</Project>

perf/benchmarkapps/GrpcClient/NamedPipeConnectionFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
namespace GrpcClient;
2323

24+
#if NET9_0_OR_GREATER
25+
2426
public class NamedPipeConnectionFactory
2527
{
2628
private readonly string _pipeName;
@@ -54,3 +56,5 @@ public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
5456
}
5557
}
5658
}
59+
60+
#endif

perf/benchmarkapps/GrpcClient/Program.cs

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Diagnostics;
2222
using System.Diagnostics.CodeAnalysis;
2323
using System.Globalization;
24+
using System.Net.Http;
2425
using System.Net.Security;
2526
using System.Net.Sockets;
2627
using System.Reflection;
@@ -78,6 +79,7 @@ public static async Task<int> Main(string[] args)
7879
var streamsOption = new Option<int>(new string[] { "--streams" }, () => 1, "Maximum concurrent streams per connection");
7980
var enableCertAuthOption = new Option<bool>(new string[] { "--enableCertAuth" }, () => false, "Flag indicating whether client sends a client certificate");
8081
var deadlineOption = new Option<int>(new string[] { "--deadline" }, "Duration of deadline in seconds");
82+
var winHttpHandlerOption = new Option<bool>(new string[] { "--winhttphandler" }, () => false, "Whether to use WinHttpHandler with Grpc.Net.Client");
8183

8284
var rootCommand = new RootCommand();
8385
rootCommand.AddOption(urlOption);
@@ -97,6 +99,7 @@ public static async Task<int> Main(string[] args)
9799
rootCommand.AddOption(streamsOption);
98100
rootCommand.AddOption(enableCertAuthOption);
99101
rootCommand.AddOption(deadlineOption);
102+
rootCommand.AddOption(winHttpHandlerOption);
100103

101104
rootCommand.SetHandler(async (InvocationContext context) =>
102105
{
@@ -118,6 +121,7 @@ public static async Task<int> Main(string[] args)
118121
_options.Streams = context.ParseResult.GetValueForOption(streamsOption);
119122
_options.EnableCertAuth = context.ParseResult.GetValueForOption(enableCertAuthOption);
120123
_options.Deadline = context.ParseResult.GetValueForOption(deadlineOption);
124+
_options.WinHttpHandler = context.ParseResult.GetValueForOption(winHttpHandlerOption);
121125

122126
var runtimeVersion = typeof(object).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "Unknown";
123127
var isServerGC = GCSettings.IsServerGC;
@@ -159,8 +163,10 @@ public static async Task<int> Main(string[] args)
159163
return await rootCommand.InvokeAsync(args);
160164
}
161165

166+
#if NET9_0_OR_GREATER
162167
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
163168
Justification = "DependencyInjection only used with safe types.")]
169+
#endif
164170
private static ILoggerFactory CreateLoggerFactory()
165171
{
166172
return LoggerFactory.Create(c =>
@@ -244,7 +250,7 @@ private static async Task StartScenario()
244250
var text = "Exception from test: " + ex.Message;
245251
Log(text);
246252
_errorStringBuilder.AppendLine();
247-
_errorStringBuilder.Append(CultureInfo.InvariantCulture, $"[{DateTime.Now:hh:mm:ss.fff}] {text}");
253+
_errorStringBuilder.Append(string.Format(CultureInfo.InvariantCulture, "[{0:hh:mm:ss.fff}] {1}", DateTime.Now, text) );
248254
}
249255
}
250256

@@ -425,7 +431,9 @@ private static void CreateChannels()
425431
var initialUri = _options.Url!;
426432
var resolvedUri = initialUri.Authority;
427433

434+
Log($"Framework version: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
428435
Log($"gRPC client type: {_options.GrpcClientType}");
436+
Log($"WinHttpHandler: {(_options.GrpcClientType == GrpcClientType.GrpcNetClient ? _options.WinHttpHandler.ToString() : "N/A")}");
429437
Log($"Log level: {_options.LogLevel}");
430438
Log($"Protocol: '{_options.Protocol}'");
431439
Log($"Creating channels to '{resolvedUri}'");
@@ -465,43 +473,12 @@ private static ChannelBase CreateChannel(string target)
465473
var address = useTls ? "https://" : "http://";
466474
address += target;
467475

468-
var httpClientHandler = new SocketsHttpHandler();
469-
httpClientHandler.UseProxy = false;
470-
httpClientHandler.AllowAutoRedirect = false;
471-
if (_options.EnableCertAuth)
472-
{
473-
var basePath = Path.GetDirectoryName(AppContext.BaseDirectory);
474-
var certPath = Path.Combine(basePath!, "Certs", "client.pfx");
475-
var clientCertificates = X509CertificateLoader.LoadPkcs12CollectionFromFile(certPath, "1111");
476-
httpClientHandler.SslOptions.ClientCertificates = clientCertificates;
477-
}
478-
479-
if (!string.IsNullOrEmpty(_options.UdsFileName))
480-
{
481-
var connectionFactory = new UnixDomainSocketConnectionFactory(new UnixDomainSocketEndPoint(ResolveUdsPath(_options.UdsFileName)));
482-
httpClientHandler.ConnectCallback = connectionFactory.ConnectAsync;
483-
}
484-
else if (!string.IsNullOrEmpty(_options.NamedPipeName))
485-
{
486-
var connectionFactory = new NamedPipeConnectionFactory(_options.NamedPipeName);
487-
httpClientHandler.ConnectCallback = connectionFactory.ConnectAsync;
488-
}
489-
490-
httpClientHandler.SslOptions.RemoteCertificateValidationCallback =
491-
(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) => true;
492-
493-
HttpMessageHandler httpMessageHandler = httpClientHandler;
494-
495-
Version? versionOverride = null;
496-
if (_options.Protocol == "h3")
497-
{
498-
// Stop gRPC channel from creating TCP socket.
499-
httpClientHandler.ConnectCallback = (context, cancellationToken) => throw new InvalidOperationException("Should never be called for H3.");
500-
501-
// Force H3 on all requests.
502-
versionOverride = new Version(3, 0);
503-
}
476+
// Force H3 on all requests.
477+
var versionOverride = _options.Protocol == "h3"
478+
? new Version(3, 0)
479+
: null;
504480

481+
var httpMessageHandler = CreateMessageHandler();
505482
return GrpcChannel.ForAddress(address, new GrpcChannelOptions
506483
{
507484
HttpHandler = httpMessageHandler,
@@ -511,6 +488,61 @@ private static ChannelBase CreateChannel(string target)
511488
}
512489
}
513490

491+
private static HttpMessageHandler CreateMessageHandler()
492+
{
493+
if (_options.WinHttpHandler)
494+
{
495+
return CreateWinHttpHandler();
496+
}
497+
498+
#if NET9_0_OR_GREATER
499+
var httpClientHandler = new SocketsHttpHandler();
500+
httpClientHandler.UseProxy = false;
501+
httpClientHandler.AllowAutoRedirect = false;
502+
if (_options.EnableCertAuth)
503+
{
504+
var basePath = Path.GetDirectoryName(AppContext.BaseDirectory);
505+
var certPath = Path.Combine(basePath!, "Certs", "client.pfx");
506+
var clientCertificates = X509CertificateLoader.LoadPkcs12CollectionFromFile(certPath, "1111");
507+
httpClientHandler.SslOptions.ClientCertificates = clientCertificates;
508+
}
509+
510+
if (!string.IsNullOrEmpty(_options.UdsFileName))
511+
{
512+
var connectionFactory = new UnixDomainSocketConnectionFactory(new UnixDomainSocketEndPoint(ResolveUdsPath(_options.UdsFileName)));
513+
httpClientHandler.ConnectCallback = connectionFactory.ConnectAsync;
514+
}
515+
else if (!string.IsNullOrEmpty(_options.NamedPipeName))
516+
{
517+
var connectionFactory = new NamedPipeConnectionFactory(_options.NamedPipeName);
518+
httpClientHandler.ConnectCallback = connectionFactory.ConnectAsync;
519+
}
520+
521+
httpClientHandler.SslOptions.RemoteCertificateValidationCallback =
522+
(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) => true;
523+
524+
if (_options.Protocol == "h3")
525+
{
526+
// Stop gRPC channel from creating TCP socket.
527+
httpClientHandler.ConnectCallback = (context, cancellationToken) => throw new InvalidOperationException("Should never be called for H3.");
528+
}
529+
530+
return httpClientHandler;
531+
#else
532+
return CreateWinHttpHandler();
533+
#endif
534+
}
535+
536+
private static WinHttpHandler CreateWinHttpHandler()
537+
{
538+
#pragma warning disable CA1416 // Validate platform compatibility
539+
return new WinHttpHandler
540+
{
541+
ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
542+
};
543+
#pragma warning restore CA1416 // Validate platform compatibility
544+
}
545+
514546
private static string ResolveUdsPath(string udsFileName) => Path.Combine(Path.GetTempPath(), udsFileName);
515547

516548
private static SslCredentials GetSslCredentials()

perf/benchmarkapps/GrpcClient/UnixDomainSocketConnectionFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
namespace GrpcClient;
2323

24+
#if NET9_0_OR_GREATER
25+
2426
public class UnixDomainSocketConnectionFactory
2527
{
2628
private readonly EndPoint _endPoint;
@@ -46,3 +48,5 @@ public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _, Canc
4648
}
4749
}
4850
}
51+
52+
#endif

0 commit comments

Comments
 (0)