Why Distributed Rate Limiting Matters
Imagine an e‑commerce flash sale where thousands of users hammer the same endpoint within seconds. Without a guard, the backend can collapse, leading to lost revenue and damaged brand reputation. Distributed rate limiting spreads the traffic control across multiple nodes, ensuring a consistent request quota regardless of where the request lands.
In 2023, Microsoft reported that 68% of high‑traffic .NET services adopted some form of rate limiting to protect microservice pipelines. Using a shared store such as Redis lets you enforce a global policy, not just per‑instance limits.
Setting Up Redis for .NET 8
Redis 7.0+ is recommended because it supports volatile‑time‑to‑live keys and efficient Lua scripting, both useful for precise throttling. Install Redis on a Linux VM or use a managed Azure Cache for Redis instance. Verify connectivity with a simple ping:
redis-cli pingThe command should return PONG, confirming the server is reachable. Remember to open port 6379 in your firewall or configure the Azure network rules accordingly.
Configuring the Distributed RateLimiter Middleware
The new RateLimiter APIs in .NET 8 integrate seamlessly with Minimal APIs. Below is a complete startup snippet that registers a Redis‑backed fixed‑window limiter and applies it globally.
var builder = WebApplication.CreateBuilder(args);<br/>builder.Services.AddStackExchangeRedisCache(options =><br/>{<br/> options.Configuration = "localhost:6379";<br/> options.InstanceName = "RateLimiterDemo";<br/>});<br/>builder.Services.AddRateLimiter(options =><br/>{<br/> options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =><br/> {<br/> var key = $"{context.Request.Path}";<br/> return RateLimitPartition.GetFixedWindowLimiter(key, _ => new FixedWindowRateLimiterOptions<br/> {<br/> PermitLimit = 100,<br/> Window = TimeSpan.FromMinutes(1),<br/> QueueProcessingOrder = QueueProcessingOrder.OldestFirst,<br/> QueueLimit = 0<br/> });<br/> });<br/> options.RejectionStatusCode = 429;<br/>});<br/>var app = builder.Build();<br/>app.UseRateLimiter();<br/>app.MapGet("/", () => "Hello World!");<br/>app.Run();Key points:
- StackExchange.Redis is the recommended client because it pools connections and supports async operations.
- PartitionedRateLimiter creates a separate limiter per request path, allowing fine‑grained control.
- PermitLimit of 100 and a one‑minute window translates to 1.6 requests per second on average, which suits most public APIs.
Testing the Limiter with Real Traffic
Use a tool like hey or wrk to fire 200 requests against the endpoint within 30 seconds. The expected outcome is 100 successful 200 responses and 100 HTTP 429 rejections.
hey -n 200 -c 20 http://localhost:5000/Inspect the response headers; the middleware adds RateLimit-Remaining and RateLimit-Reset values, which client applications can read to implement back‑off logic.
Best Practices and Pitfalls
1. Warm‑up Redis connections. The first few requests may suffer latency spikes while the connection pool initializes. Pre‑load the cache in Program.cs to avoid this.
2. Choose the right limiter type. Fixed‑window is simple but can cause burst traffic at window boundaries. Sliding‑window or token‑bucket algorithms provide smoother throttling at the cost of extra Redis Lua scripts.
3. Monitor Redis memory. Each limiter key occupies memory until it expires. With 10,000 distinct API routes, set an appropriate KeyExpiration (e.g., 2 × window size) to prevent leaks.
4. Graceful degradation. If Redis becomes unavailable, fallback to a local in‑memory limiter with a stricter quota to keep the service alive while alerting ops teams.
Sources
Microsoft Docs – ASP.NET Core Rate Limiting Middleware
Redis.io – Official Redis Documentation
StackOverflow – Community examples of PartitionedRateLimiter with Redis
Author: Mahmut Sarıkaya — sarikayadev.com