🚀 ASP.NET Performance & Resilience: Make Your App a Superhero!
Imagine your app is a race car. Right now, it might be a nice family car. But we’re about to turn it into a Formula 1 champion—faster, lighter, and tough enough to handle anything the road throws at it!
🎯 What We’ll Learn
We’re going to make your ASP.NET app:
- Start faster (AOT & Native AOT)
- Weigh less (Trimming)
- Talk faster (HTTP/2 & HTTP/3)
- Never give up (Polly resilience patterns)
- Stay in control (Timeouts & Rate Limiting)
🏎️ Part 1: AOT Compilation — Pre-Cooking Your Code
What is AOT?
AOT = Ahead-Of-Time Compilation
Think of it like this:
Without AOT: You order pizza. The chef makes it when you call. Takes time.
With AOT: The pizza is already made and waiting. Just grab and eat!
Normally, .NET compiles your code while your app runs (JIT = Just-In-Time). With AOT, the code is compiled before you even start the app.
Why Use AOT?
| Benefit | What It Means |
|---|---|
| ⚡ Faster startup | App starts instantly |
| 📦 Smaller memory | Uses less RAM |
| 🔒 Predictable | No surprise slowdowns |
Simple Example
<!-- In your .csproj file -->
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
Then publish:
dotnet publish -c Release
That’s it! Your app is now pre-cooked and ready to serve! 🍕
🦾 Part 2: Native AOT — The Ultimate Speed Machine
What’s the Difference?
Native AOT takes AOT even further:
Regular AOT: Pre-cooked pizza in a box
Native AOT: Pre-cooked pizza already on your plate, fork in hand!
With Native AOT, your app becomes a single executable file that runs without needing .NET installed!
The Magic
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -r win-x64 -c Release
What You Get
graph TD A["Your C# Code"] --> B["Native AOT Compiler"] B --> C["Single .exe File"] C --> D["Runs Anywhere!"] D --> E["No .NET Runtime Needed"]
⚠️ Some Rules
Native AOT is powerful but has limits:
- ❌ No runtime code generation
- ❌ No
dynamickeyword - ❌ Limited reflection
- ✅ Perfect for APIs and microservices!
✂️ Part 3: Trimming — Put Your App on a Diet
What is Trimming?
Imagine your app is a backpack. You packed everything—but you only need a water bottle and snacks.
Trimming removes the stuff you don’t use!
Before trimming: 50 MB backpack 🎒
After trimming: 5 MB fanny pack 👛
How to Trim
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Trimming Levels
| Mode | What It Does |
|---|---|
partial |
Gentle trim, keeps more |
link |
Aggressive trim, removes unused code |
full |
Maximum trim (Native AOT only) |
⚠️ Be Careful!
Trimming can accidentally remove code you need (like reflection-based code). Test thoroughly!
🌐 Part 4: HTTP/2 — Two Lanes Are Better Than One
The Traffic Problem
HTTP/1.1 is like a one-lane road:
- One car (request) at a time
- Cars wait in line
- Traffic jams! 🚗🚗🚗
HTTP/2 is like a highway:
- Many cars at once!
- No waiting
- Zoom zoom! 🏎️💨
Key Features
graph TD A["HTTP/2 Benefits"] --> B["Multiplexing"] A --> C["Header Compression"] A --> D["Server Push"] B --> E["Many requests on 1 connection"] C --> F["Less data sent"] D --> G["Server sends before you ask"]
Enable HTTP/2 in Kestrel
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.Protocols =
HttpProtocols.Http2;
listenOptions.UseHttps();
});
});
⚡ Part 5: HTTP/3 — The Future is Here!
What’s New?
HTTP/3 uses QUIC instead of TCP.
TCP is like a phone call—you wait for “hello” before talking.
QUIC is like texting—just send it!
Why HTTP/3 Rocks
| Feature | Benefit |
|---|---|
| 🚀 Faster connections | No handshake delays |
| 📱 Better for mobile | Handles network changes |
| 🔒 Built-in encryption | Always secure |
Enable HTTP/3
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.Protocols =
HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.UseHttps();
});
});
🛡️ Part 6: Resilience with Polly — Never Give Up!
What is Polly?
Polly is your app’s superhero sidekick. When things go wrong, Polly helps your app:
- Retry when something fails
- Wait when services are busy
- Have a backup plan when all else fails
Install Polly
dotnet add package Microsoft.Extensions.Http.Resilience
The Resilience Patterns
graph TD A["Polly Patterns"] --> B["Retry"] A --> C["Circuit Breaker"] A --> D["Timeout"] A --> E["Fallback"] B --> F["Try again if it fails"] C --> G["Stop trying if broken"] D --> H["Give up after X seconds"] E --> I["Use backup if nothing works"]
Retry Example
// Try 3 times before giving up
builder.Services
.AddHttpClient("MyApi")
.AddStandardResilienceHandler();
Circuit Breaker — The Smart Switch
Imagine a light switch that turns itself OFF when there’s a fire:
builder.Services.AddHttpClient("MyApi")
.AddResilienceHandler("circuit", builder =>
{
builder.AddCircuitBreaker(new()
{
FailureRatio = 0.5,
MinimumThroughput = 10,
BreakDuration = TimeSpan
.FromSeconds(30)
});
});
50% failures? Circuit opens. Wait 30 seconds. Try again.
⏰ Part 7: Request Timeout Middleware — Patience Has Limits!
Why Timeouts Matter
Imagine waiting 10 minutes for a webpage. You’d leave, right?
Timeouts say: “If you can’t answer in X seconds, forget it!”
Built-in Timeout Middleware (.NET 8+)
// Add the service
builder.Services.AddRequestTimeouts();
var app = builder.Build();
// Use the middleware
app.UseRequestTimeouts();
Set Timeouts
// Global default: 30 seconds
builder.Services.AddRequestTimeouts(options =>
{
options.DefaultPolicy = new RequestTimeoutPolicy
{
Timeout = TimeSpan.FromSeconds(30)
};
});
// Per-endpoint
app.MapGet("/slow", async () =>
{
await Task.Delay(60000);
return "Done!";
})
.WithRequestTimeout(TimeSpan.FromSeconds(5));
Now
/slowwill timeout after 5 seconds instead of waiting forever!
🚦 Part 8: Rate Limiting — Don’t Flood the Restaurant!
The Problem
Imagine 1 million people trying to order pizza at the same restaurant. The kitchen explodes! 💥
Rate limiting = Only 100 orders per minute, please!
Add Rate Limiting
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter(
"fixed",
config =>
{
config.Window =
TimeSpan.FromMinutes(1);
config.PermitLimit = 100;
config.QueueLimit = 0;
});
});
var app = builder.Build();
app.UseRateLimiter();
Rate Limiting Types
| Type | How It Works |
|---|---|
| Fixed Window | 100 requests per minute, resets every minute |
| Sliding Window | Smooth, rolling time window |
| Token Bucket | Refills tokens over time |
| Concurrency | Only X requests at once |
Example: Token Bucket
options.AddTokenBucketLimiter("token", config =>
{
config.TokenLimit = 100;
config.ReplenishmentPeriod =
TimeSpan.FromSeconds(10);
config.TokensPerPeriod = 10;
});
Start with 100 tokens. Use 1 per request. Get 10 new tokens every 10 seconds.
🎉 Putting It All Together
Your ASP.NET app is now:
| Feature | What It Does |
|---|---|
| ✂️ Trimmed | Lightweight, only what you need |
| 🚀 Native AOT | Blazing fast startup |
| 🌐 HTTP/2 & 3 | Lightning-fast communication |
| 🛡️ Polly | Resilient, never gives up |
| ⏰ Timeouts | Won’t wait forever |
| 🚦 Rate Limits | Won’t get overwhelmed |
graph TD A["Your App"] --> B["Trimmed & AOT"] B --> C["Fast Startup"] A --> D["HTTP/2 & HTTP/3"] D --> E["Fast Communication"] A --> F["Polly Resilience"] F --> G["Never Crashes"] A --> H["Timeouts & Rate Limits"] H --> I["Always In Control"] C --> J["🏆 SUPERHERO APP!"] E --> J G --> J I --> J
🧠 Quick Recap
- AOT = Pre-compile your code before running
- Native AOT = Single executable, no .NET needed
- Trimming = Remove unused code, smaller app
- HTTP/2 = Multiple requests, one connection
- HTTP/3 = QUIC protocol, even faster!
- Polly = Retry, circuit breaker, fallback
- Timeouts = Don’t wait forever
- Rate Limiting = Control traffic flow
You did it! 🎊 Your app is now faster, lighter, and tougher than ever. Go build something amazing!
