Rate Limiting & API Throttling

লেসন ৪৩ / ৫০

কেন Rate Limit?

DDoS, brute-force, abuse-এর বিরুদ্ধে সুরক্ষা। Free tier user-কে limit করুন।

Built-in Rate Limiting (.NET 7+)

using System.Threading.RateLimiting;

builder.Services.AddRateLimiter(opt => {
    opt.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    // Fixed Window — 100 req/min
    opt.AddFixedWindowLimiter("fixed", o => {
        o.PermitLimit = 100;
        o.Window = TimeSpan.FromMinutes(1);
        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        o.QueueLimit = 10;
    });

    // Sliding Window — smoother
    opt.AddSlidingWindowLimiter("sliding", o => {
        o.PermitLimit = 100;
        o.Window = TimeSpan.FromMinutes(1);
        o.SegmentsPerWindow = 6;
    });

    // Token Bucket — burst allow করে
    opt.AddTokenBucketLimiter("token", o => {
        o.TokenLimit = 100;
        o.TokensPerPeriod = 10;
        o.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
        o.AutoReplenishment = true;
    });

    // Concurrency — একসাথে কত request
    opt.AddConcurrencyLimiter("concurrency", o => {
        o.PermitLimit = 5;
        o.QueueLimit = 10;
    });
});

var app = builder.Build();
app.UseRateLimiter();

Apply Globally / Per Endpoint

// Global
app.MapControllers().RequireRateLimiting("fixed");

// Per controller
[EnableRateLimiting("token")]
public class PaymentsController : ControllerBase { ... }

// Per action
[EnableRateLimiting("fixed")]
[HttpPost("login")]
public IActionResult Login(...) { ... }

// Disable for specific
[DisableRateLimiting]
[HttpGet("health")]
public IActionResult Health() => Ok();

Per-User / Per-IP Partitioning

opt.AddPolicy("per-user", ctx => {
    var userId = ctx.User.Identity?.Name ?? ctx.Connection.RemoteIpAddress?.ToString() ?? "anon";
    return RateLimitPartition.GetFixedWindowLimiter(userId, _ => new FixedWindowRateLimiterOptions {
        PermitLimit = 60,
        Window = TimeSpan.FromMinutes(1)
    });
});

[EnableRateLimiting("per-user")]
public IActionResult Get() => ...

Custom Response (429)

opt.OnRejected = async (ctx, ct) => {
    ctx.HttpContext.Response.StatusCode = 429;
    if (ctx.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retry)) {
        ctx.HttpContext.Response.Headers.RetryAfter = retry.TotalSeconds.ToString();
    }
    await ctx.HttpContext.Response.WriteAsJsonAsync(new {
        error = "Too many requests. Try later."
    }, ct);
};

AspNetCoreRateLimit (Pre-.NET 7 Library)

// Older alternative
dotnet add package AspNetCoreRateLimit
// IP-based, client-id based limits via appsettings