A production-ready, highly scalable and configurable distributed rate limiter for ASP.NET Core built with .NET 10. Implements multiple rate-limiting algorithms using the Strategy pattern, supports Redis for distributed deployments, and includes comprehensive monitoring with Prometheus metrics.
✅ Multiple Algorithms
- Token Bucket (in-memory & distributed via Redis)
- Fixed Window Counter
- Sliding Window Log
- Leaky Bucket
✅ Distributed Support
- Redis-backed repository with connection pooling
- Atomic operations via Lua scripts
- Distributed token bucket algorithm
- Multi-server deployment ready
✅ Configurable Rules
- Limit by User ID, IP Address, Client ID
- Custom property-based limiting
- Priority-based rule matching
- Dynamic rule reloading (no restart required)
✅ Monitoring & Observability
- Prometheus metrics integration
- Serilog structured logging
- Detailed logging for all decisions
- Metrics export endpoint
✅ Admin API
- CRUD operations for rate limit rules
- Dynamic rule management
- Metrics endpoint
- No downtime rule updates
✅ Quality Assurance
- Comprehensive unit tests (xUnit)
- Integration tests with WebApplicationFactory
- SOLID principles & design patterns
- Repository pattern for data access
Client
│
▼
ASP.NET Middleware
│
▼
Rate Limiter Service
│
┌───────────┼───────────┐
▼ ▼ ▼
Token Bucket Sliding Window Fixed Window
LeakyBucket
│ │ │
└───────────┼───────────┘
▼
┌──────────────┴──────────────┐
▼ ▼
In-Memory Redis Repository
Repository (Distributed)
│ │
└──────────────┬──────────────┘
▼
HTTP Response
RateLimiter/
├── Core/
│ ├── IRateLimiter.cs
│ ├── IRateLimitStrategy.cs
│ ├── IRuleProvider.cs
│ ├── DefaultRateLimiter.cs
│ ├── LoggingRateLimiterDecorator.cs
│ ├── RateLimitStrategyFactory.cs
│ ├── Models/
│ │ ├── RateLimitRequest.cs
│ │ ├── RateLimitRule.cs
│ │ ├── RateLimitResult.cs
│ │ └── RateLimiterOptions.cs
│ └── Exceptions/
│ └── RateLimitException.cs
│
├── Algorithms/
│ ├── TokenBucketStrategy.cs
│ ├── DistributedTokenBucketStrategy.cs
│ ├── FixedWindowStrategy.cs
│ ├── SlidingWindowStrategy.cs
│ └── LeakyBucketStrategy.cs
│
├── Infrastructure/
│ ├── IRateLimitRepository.cs
│ ├── InMemoryRateLimitRepository.cs
│ ├── RedisRateLimitRepository.cs
│ ├── Logging/
│ │ └── SerilogConfiguration.cs
│ └── Exceptions/
│ └── RedisConnectionException.cs
│
├── RuleEngine/
│ └── JsonRuleProvider.cs
│
├── Middleware/
│ └── RateLimitingMiddleware.cs
│
├── Controllers/
│ ├── TestController.cs
│ └── AdminController.cs
│
├── Monitoring/
│ └── MetricsService.cs
│
├── Program.cs
├── appsettings.json
└── RateLimiter.csproj
- .NET 10 SDK
- Redis (optional, for distributed deployments)
- Visual Studio 2026 or VS Code
- Clone or navigate to the workspace
- Restore packages:
dotnet restore- Build the solution:
dotnet build{
"RateLimiter": {
"DefaultAlgorithm": "TokenBucket",
"Rules": [
{
"RuleId": "user_limit",
"Property": "User",
"Limit": 100,
"Window": "00:01:00",
"Algorithm": "TokenBucket",
"Priority": 1,
"Enabled": true
},
{
"RuleId": "ip_limit",
"Property": "IP",
"Limit": 1000,
"Window": "00:01:00",
"Algorithm": "FixedWindow",
"Priority": 10,
"Enabled": true
}
]
},
"Redis": {
"Enabled": false,
"ConnectionString": "localhost:6379",
"Ssl": false,
"AbortOnConnectFail": false
},
"Serilog": {
"MinimumLevel": "Information",
"WriteTo": [ ... ]
}
}- User: Limit by authenticated user ID
- IP: Limit by client IP address
- Client: Limit by X-Client-Id header
- Custom: Any custom identifier
- Best for: Bursting traffic patterns, AWS-like behavior
- In-Memory: LocalTokenBucketStrategy
- Distributed: DistributedTokenBucketStrategy (Redis + Lua)
- Characteristics: Allows bursts, smooth refill
- Best for: Simple rate limiting
- Characteristics: Counter resets every window period
- Issue: Boundary problem (spike at window edges)
- Best for: Accurate limiting
- Characteristics: Keeps log of all requests in window
- Trade-off: Higher memory usage
- Best for: Smooth traffic control
- Characteristics: Requests leak at constant rate
- Issue: Cannot burst
dotnet run --project RateLimiter.csprojThe application will:
- Start on https://localhost:5001
- Use in-memory repository
- Apply rules from appsettings.json
- Start Redis:
docker run -d -p 6379:6379 redis:latest- Update appsettings.json:
{
"Redis": {
"Enabled": true,
"ConnectionString": "localhost:6379"
}
}-
Update Program.cs to register RedisRateLimitRepository and DistributedTokenBucketStrategy
-
Run:
dotnet run --project RateLimiter.csprojGET /api/test
Returns: { "message": "ok" }
Response Headers:
X-RateLimit-Limit: Max requests allowedX-RateLimit-Remaining: Remaining requestsRetry-After: Seconds to wait (on rate limit)
GET /api/admin/rules
GET /api/admin/rules/{ruleId}
POST /api/admin/rules
Content-Type: application/json
{
"ruleId": "new_rule",
"property": "User",
"limit": 50,
"window": "00:01:00",
"algorithm": "TokenBucket",
"priority": 5,
"enabled": true
}
PUT /api/admin/rules/{ruleId}
Content-Type: application/json
{
"limit": 75,
"enabled": false
}
DELETE /api/admin/rules/{ruleId}
GET /api/admin/metrics
GET /metrics (Prometheus endpoint)
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 30
{
"message": "Rate limit exceeded",
"retryAfter": 30
}
dotnet testdotnet test --filter "Category=Unit"dotnet test --filter "Category=Integration"Unit Tests:
- TokenBucketStrategy
- FixedWindowStrategy
- SlidingWindowStrategy
- LeakyBucketStrategy
- InMemoryRateLimitRepository
- RateLimitStrategyFactory
Integration Tests:
- Middleware behavior
- Rule loading
- Admin endpoints
- End-to-end flows
Available at: http://localhost:5000/metrics
Counters:
ratelimit_allowed_requests_total: Total allowed requestsratelimit_blocked_requests_total: Total blocked requestsratelimit_errors_total: Total errors
Gauges:
ratelimit_active_keys: Current number of tracked keys
Histograms:
ratelimit_request_duration_seconds: Request processing duration
Logs are written to:
- Console (formatted JSON)
- File:
logs/ratelimiter-YYYY-MM-DD.txt
Log format:
{
"Timestamp": "2024-01-15T10:30:45.123Z",
"Level": "Information",
"Message": "RateLimit decision: Allowed=true Remaining=47 Limit=100 RetryAfter=0",
"Application": "RateLimiter",
"MachineName": "DESKTOP-ABC"
}Algorithms can be swapped without changing application code.
RateLimitStrategyFactory creates appropriate strategy instances.
IRateLimitRepository abstracts data store (in-memory or Redis).
LoggingRateLimiterDecorator adds logging without modifying core logic.
ASP.NET Core DI container manages all service lifetimes.
| Algorithm | Memory | Accuracy | Supports Burst | Time Complexity |
|---|---|---|---|---|
| Token Bucket | Low | High | Yes | O(1) |
| Fixed Window | Very Low | Medium | No | O(1) |
| Sliding Window Log | Medium-High | Very High | No | O(n) |
| Leaky Bucket | Low | High | No | O(1) |
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY . .
EXPOSE 5000
ENV ASPNETCORE_URLS=http://+:5000
CMD ["dotnet", "RateLimiter.dll"]Build and run:
docker build -t rate-limiter:latest .
docker run -p 5000:5000 -e Redis__Enabled=true rate-limiter:latestEnvironment variables:
env:
- name: Redis__Enabled
value: "true"
- name: Redis__ConnectionString
value: "redis-service:6379"
- name: ASPNETCORE_ENVIRONMENT
value: "Production"ASPNETCORE_ENVIRONMENT: Development, Staging, ProductionRedis__Enabled: true/falseRedis__ConnectionString: Redis connection stringLogging__LogLevel__Default: Debug, Information, Warning, Error
-
Choose the Right Algorithm
- Use Token Bucket for APIs with burst traffic
- Use Fixed Window for simple rate limiting
- Use Sliding Window for very accurate limiting
-
Set Appropriate Limits
- Monitor usage patterns before setting limits
- Use admin API to adjust dynamically
- Set priority for rule matching order
-
Monitoring
- Enable Prometheus metrics in production
- Monitor blocked request count
- Set up alerts for error spikes
-
Failover
- If Redis unavailable, Redis repository logs error
- Middleware fails open (allows request)
- Application continues working
-
Security
- Protect admin endpoints with authentication
- Validate X-Client-Id header sources
- Use TLS for Redis connections in production
- Check if Sliding Window strategy is used
- Monitor active keys count in Prometheus
- Consider switching to Token Bucket or Fixed Window
- Check rule.Enabled = true
- Verify rule.Property matches request (User, IP, Client)
- Check appsettings.json configuration
- View logs for matching decisions
- Verify Redis is running
- Check ConnectionString in appsettings.json
- Ensure firewall allows port 6379
- Check Redis logs
Check rate limit:
curl -i http://localhost:5000/api/test \
-H "X-Client-Id: client123"View rules:
curl http://localhost:5000/api/admin/rulesCreate rule:
curl -X POST http://localhost:5000/api/admin/rules \
-H "Content-Type: application/json" \
-d '{
"ruleId": "api_limit",
"property": "IP",
"limit": 50,
"window": "00:01:00",
"algorithm": "TokenBucket",
"priority": 1,
"enabled": true
}'using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("X-Client-Id", "client123");
var response = await client.GetAsync("http://localhost:5000/api/test");
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
var retryAfter = response.Headers.GetValues("Retry-After").First();
Console.WriteLine($"Rate limited. Retry after {retryAfter} seconds");
}
}- Create feature branch
- Add/update tests
- Ensure all tests pass
- Submit pull request
MIT License - See LICENSE file for details
- Full source code comments and XML docs
- RateLimiter.md for detailed specifications
- Test project for usage examples
- Admin API for real-time rule management
- .NET 10
- ASP.NET Core 10
- StackExchange.Redis 2.7.27
- Serilog 4.1.1
- Prometheus.NET 8.2.1
- xUnit 2.9.3
- FluentAssertions 6.12.0
- Moq 4.20.70
- Circuit breaker pattern
- Advanced analytics dashboard
- Machine learning-based limit adjustment
- GraphQL admin API
- WebSocket support
- Rate limit sharing between services
- Multi-region failover
Version: 1.0.0
Last Updated: 2024
Author: Rate Limiter Team