High Performance Networking in .NET with Kestrel and YARP

Search for a command to run...

No comments yet. Be the first to comment.
An authenticated user calls your API with a valid access token. The token contains the right role and the endpoint requires authorisation. Everything appears secure. The user then changes /claims/8d61

Sending text to an AI model as an image sounds like it should cost more. The application has to render the text, encode a PNG and send a much larger payload over the network. Yet for the right kind of

Applications increasingly accept URLs from outside their own code. A webhook tester calls an endpoint supplied by a customer. An image service downloads a remote avatar. A document pipeline retrieves

Some pull requests look harmless until they reach production. Then someone notices the change touched authentication, a shared response contract, an EF Core migration, and one production config value

IRC is old, plain, and surprisingly useful as a teaching tool. You open a TCP connection. You send text commands. The server sends text lines back. The connection stays open. If the server sends PING,

I've continuously sought effective solutions for building high performance applications. Among the various technologies I've worked with, Kestrel and YARP have emerged as powerful tools, particularly when optimising network performance and scalability in .NET applications. Below, I'll share deeper insights and advanced techniques that I've discovered and refined through experience, aiming to help you harness these powerful frameworks effectively.
Kestrel's introduction was transformative for the ASP.NET community. Built specifically for high performance, Kestrel supports asynchronous request handling, dramatically improving scalability. My initial adoption of Kestrel required an understanding beyond basic configurations to achieve enterprise level performance.
I soon realised that tuning connection limits, keep alive settings, and request buffering had a direct impact on throughput. These optimisations significantly improved handling large spikes in web traffic without compromising service reliability.
An example of optimising Kestrel's configurations:
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 200_000;
serverOptions.Limits.MaxConcurrentUpgradedConnections = 20_000;
serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(3);
serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(60);
serverOptions.Limits.Http2.MaxStreamsPerConnection = 100;
});
In large distributed applications, managing multiple microservices becomes complex. My experience deploying YARP showed me its exceptional capabilities for request routing, load balancing, and dynamic configuration.
A particularly valuable feature of YARP is its advanced session affinity, which allows sticky sessions to particular servers, enhancing performance for stateful services. Here's a look at configuring session affinity:
"Clusters": {
"webCluster": {
"SessionAffinity": {
"Enabled": true,
"Policy": "Cookie",
"Cookie": {
"Name": "AffinityCookie",
"Expiration": "00:30:00"
}
},
"Destinations": {
"server1": {"Address": "https://localhost:7001"},
"server2": {"Address": "https://localhost:7002"}
}
}
}
Implementing affinity was crucial in maintaining user state without extensive overhead in our user intensive applications.
Load balancing in YARP can be customised extensively. I’ve found customising the load balancing strategy to fit specific application needs can lead to substantial performance gains.
An example of configuring a round robin load balancer with custom health checks:
"HealthCheck": {
"Active": {
"Enabled": true,
"Interval": "00:00:10",
"Timeout": "00:00:05",
"Policy": "ConsecutiveFailures",
"FailureThreshold": 3
}
},
"LoadBalancing": {
"Policy": "RoundRobin"
}
Through iterative testing, I discovered that tuning the health check interval and thresholds was pivotal in maintaining optimal load distribution and quick recovery from outages.
Achieving high performance isn't only about initial setup but also about maintaining visibility. I've integrated structured logging and distributed tracing through OpenTelemetry, enabling detailed diagnostics and facilitating quicker issue resolution.
Here's an example of integrating OpenTelemetry with Kestrel:
builder.Services.AddOpenTelemetryTracing(builder =>
{
builder
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyKestrelApp"))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddConsoleExporter();
});
This setup provided crucial insights during performance tuning, helping identify bottlenecks and latencies precisely.
Security often impacts performance. Initially, configuring HTTPS introduced performance overhead, but fine tuning certificate handling and connection settings allowed me to mitigate these effects.
Optimising certificate loading and SSL configurations:
serverOptions.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
CertificateLoader.LoadFromStoreCert("MyCertificate", "MyStore", StoreLocation.CurrentUser, true);
});
});
Through careful management of certificates and encryption settings, performance impact was reduced significantly without compromising security.
Real deployment scenarios have continually taught me invaluable lessons. For instance, leveraging the reverse proxy cache of YARP dramatically reduced latency and server load:
"Caching": {
"Enabled": true,
"ExpirationTime": "00:05:00"
}
Caching also significantly reduced response times and improved client experience during peak traffic hours.
Performance is not static; regular benchmarking has become integral to my process. Using BenchmarkDotNet for micro benchmarks and Apache JMeter for load testing, I've maintained and continually improved performance.
BenchmarkDotNet example for a basic HTTP endpoint:
[Benchmark]
public async Task TestEndpointPerformance()
{
var response = await _httpClient.GetAsync("https://localhost:5001/api/data");
response.EnsureSuccessStatusCode();
}
Regular testing and analysis informed ongoing optimisations and ensured consistent high performance.
My experience with Kestrel and YARP has revealed a wealth of potential when building high performance, scalable .NET applications. By mastering advanced configuration, observability, security, and continuous benchmarking, I've managed to build systems resilient to heavy loads and capable of maintaining exceptional performance.
The ideas outlined here come from practical experience, experimentation, and iterative learning. My advice is immerse yourself deeply in these tools, continually refine your approach, play around, and never underestimate the value of thorough testing and monitoring.