Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Von Arvin Kahbazi, Maarten Balliauw und Rick Anderson
Die Microsoft.AspNetCore.RateLimiting-Middleware bietet Middleware mit Bandbreitenbegrenzung. Apps konfigurieren Richtlinien zur Begrenzung der Bandbreite und wenden dann die Richtlinien auf Endpunkten an. Apps mit Ratenbegrenzung sollten vor der Bereitstellung sorgfältigen Auslastungstests und Überprüfungen unterzogen werden. Weitere Informationen finden Sie unter Testen von Endpunkten mit Ratenbegrenzung in diesem Artikel.
Eine Einführung in die Ratelimitierung finden Sie unter Middleware mit Bandbreitenbegrenzung.
Gründe für die Verwendung von Ratenbeschränkungen
Die Häufigkeitsbeschränkung kann zum Verwalten des Flusses eingehender Anforderungen an eine App verwendet werden. Wichtige Gründe für die Implementierung der Zinsbegrenzung:
- Missbrauch verhindern: Die Beschränkung der Rate trägt dazu bei, eine App vor Missbrauch zu schützen, indem die Anzahl der Anforderungen beschränkt wird, die ein Benutzer oder Client in einem bestimmten Zeitraum vornehmen kann. Dies ist besonders wichtig für öffentliche APIs.
- Gewährleistung der fairen Nutzung: Durch die Festlegung von Grenzwerten haben alle Benutzer einen fairen Zugang zu Ressourcen und verhindern, dass Die Benutzer das System monopolisieren.
- Schützen von Ressourcen: Durch die Einschränkung der Geschwindigkeit wird verhindert, dass serverüberlastet ist, indem die Anzahl der anforderungen gesteuert wird, die verarbeitet werden können, wodurch die Back-End-Ressourcen nicht überfordert werden.
- Verbesserung der Sicherheit: Es kann das Risiko von Denial of Service (DoS)-Angriffen verringern, indem die Häufigkeit beschränkt wird, mit der Anforderungen verarbeitet werden, wodurch angreifern das Überfluten eines Systems erschwert werden.
- Verbesserung der Leistung: Durch die Steuerung der Häufigkeit eingehender Anforderungen kann die optimale Leistung und Reaktionsfähigkeit einer App beibehalten werden, um eine bessere Benutzererfahrung sicherzustellen.
- Kostenmanagement: Bei Diensten, die auf der Grundlage der Nutzung kostenaufwendigen, kann die Zinsbegrenzung dazu beitragen, Ausgaben zu verwalten und vorherzusagen, indem das Volumen der verarbeiteten Anforderungen gesteuert wird.
Die Implementierung von Ratenbeschränkungen in einer ASP.NET Core-App kann dazu beitragen, Stabilität, Sicherheit und Leistung aufrechtzuerhalten und einen zuverlässigen und effizienten Dienst für alle Benutzer sicherzustellen.
Verhindern von DDoS-Angriffen
Während die Zinsbegrenzung dazu beitragen kann, das Risiko von Denial of Service -Angriffen (DoS) zu verringern, indem die Rate beschränkt wird, mit der Anforderungen verarbeitet werden, ist es keine umfassende Lösung für DDoS-Angriffe (Distributed Denial of Service). DDoS-Angriffe umfassen mehrere Systeme, die eine App mit einer Flut von Anforderungen überwältigen, was es schwierig macht, mit der Ratenbegrenzung allein zu umgehen.
Für einen robusten DDoS-Schutz sollten Sie einen kommerziellen DDoS-Schutzdienst verwenden. Diese Dienste bieten erweiterte Features wie:
- Datenverkehrsanalyse: Kontinuierliche Überwachung und Analyse eingehender Datenverkehr zur Erkennung und Entschärfung von DDoS-Angriffen in Echtzeit.
- Skalierbarkeit: Die Möglichkeit, große Angriffe zu bewältigen, indem der Datenverkehr über mehrere Server und Rechenzentren verteilt wird.
- Automatisierte Entschärfung: Mit automatisierten Reaktionsmechanismen kann bösartiger Datenverkehr schnell und ohne manuelles Eingreifen blockiert werden.
- Globales Netzwerk: Ein globales Netzwerk von Servern, um Angriffe zu absorbieren und zu mindern, die der Quelle näher sind.
- Ständige Updates: Kommerzielle Dienste verfolgen und aktualisieren kontinuierlich ihre Schutzmechanismen, um sich an neue und sich entwickelnde Bedrohungen anzupassen.
Bei Verwendung eines Cloudhostingdiensts ist DDoS-Schutz in der Regel als Teil der Hostinglösung verfügbar, z. B. Azure Web Application Firewall, AWS Shield oder Google Cloud Armor. Dedizierte Schutzmaßnahmen sind als Webanwendungsfirewalls (WAF) oder als Teil einer CDN-Lösung wie Cloudflare oder Akamai Kona Site Defender verfügbar.
Die Implementierung eines kommerziellen DDoS-Schutzdiensts in Verbindung mit der Zinsbegrenzung kann eine umfassende Verteidigungsstrategie bieten, um die Stabilität, Sicherheit und Leistung einer App sicherzustellen.
Verwenden von Middleware zum Einschränken von Raten
Die folgenden Schritte zeigen, wie Sie die Ratelimitierung von Middleware in einer ASP.NET Core-App verwenden:
- Konfigurieren Sie Dienste zur Begrenzung der Rate.
Konfigurieren Sie in der Program.cs Datei die Dienste zum Einschränken von Raten, indem Sie die entsprechenden Richtlinien für die Ratenbegrenzung hinzufügen. Richtlinien können entweder als globale oder benannte Richtlinien definiert werden. Im folgenden Beispiel werden 10 Anforderungen pro Minute nach Benutzer (Identität) oder global zulässig:
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
QueueLimit = 0,
Window = TimeSpan.FromMinutes(1)
}));
});
Benannte Richtlinien müssen explizit auf die Seiten oder Endpunkte angewendet werden. Im folgenden Beispiel wird eine feste Fensterbegrenzungspolitik namens "fixed" hinzugefügt, die später mit einem Endpunkt verknüpft wird.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", opt =>
{
opt.PermitLimit = 4;
opt.Window = TimeSpan.FromSeconds(12);
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 2;
});
});
var app = builder.Build();
Der globale Grenzwert gilt automatisch für alle Endpunkte, wenn er über Optionen konfiguriert wird . GlobalLimiter.
Aktivierung von Middleware zur Ratenbegrenzung
Aktivieren Sie in der
Program.csDatei die Ratelimitierung der Middleware durch Aufrufen von UseRateLimiter:
app.UseRouting();
app.UseRateLimiter();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
Richtlinien zur Ratenbegrenzung auf Endpunkte oder Seiten anwenden
Anwenden von Ratenbeschränkungen auf WebAPI-Endpunkte
Wenden Sie eine benannte Richtlinie auf den Endpunkt oder die Gruppe an, z. B.:
app.MapGet("/api/resource", () => "This endpoint is rate limited")
.RequireRateLimiting("fixed"); // Apply specific policy to an endpoint
Anwenden von Ratenbeschränkungen auf MVC-Controller
Wenden Sie die konfigurierten Richtlinien zur Begrenzung der Rate auf bestimmte Endpunkte oder global an. Zum Beispiel, um die Richtlinie "fest" auf alle Controller-Endpunkte anzuwenden:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireRateLimiting("fixed");
});
Anwenden von Ratenbeschränkungen auf serverseitige Blazor-Apps
Um die Ratenbegrenzung für alle routingfähigen Razor Komponenten der App festzulegen, geben Sie RequireRateLimiting mit dem Namen der Richtlinie zur Ratenbegrenzung beim Aufruf MapRazorComponents in der Program-Datei an. Im folgenden Beispiel wird die Richtlinie zur Begrenzung der Rate mit dem Namen "policy" angewendet:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.RequireRateLimiting("policy");
Um eine Richtlinie für eine einzelne routingfähige Razor Komponente oder einen Ordner mit Komponenten über eine _Imports.razor Datei festzulegen, wird das [EnableRateLimiting] Attribut mit dem Richtliniennamen angewendet. Im folgenden Beispiel wird die Richtlinie zur Begrenzung der Rate mit dem Namen "override" angewendet. Die Richtlinie ersetzt alle Richtlinien, die derzeit auf den Endpunkt angewendet werden. Der globale Grenzwert wird weiterhin auf dem Endpunkt ausgeführt, auf den dieses Attribut angewendet wurde.
@page "/counter"
@using Microsoft.AspNetCore.RateLimiting
@attribute [EnableRateLimiting("override")]
<h1>Counter</h1>
Das [EnableRateLimiting]Attribut wird nur dann über eine _Imports.razorDatei auf eine routingfähige Komponente oder einen Ordner mit Komponenten angewendet, wenn RequireRateLimitingnicht auf MapRazorComponents aufgerufen wird.
Das [DisableRateLimiting] Attribut wird verwendet, um die Ratebegrenzung für eine routingfähige Komponente oder einen Ordner mit Komponenten über eine _Imports.razor Datei zu deaktivieren.
Ratenbegrenzungsalgorithmen
Die RateLimiterOptionsExtensions-Klasse stellt die folgenden Erweiterungsmethoden für die Ratenbegrenzung bereit:
Die Begrenzungen mit festem Zeitfenster, gleitendem Zeitfenster und Tokenbucket begrenzen alle die maximale Anzahl von Anforderungen in einem bestimmten Zeitraum. Die Parallelitätsbegrenzung schränkt nur die Anzahl gleichzeitiger Anforderungen ein und begrenzt nicht die Gesamtanzahl der Anforderungen in einem Zeitraum. Die Kosten eines Endpunkts sollten bei der Auswahl einer Begrenzung berücksichtigt werden. Die Kosten eines Endpunkts umfassen die verwendeten Ressourcen, z. B. Zeit, Datenzugriff, CPU und E/A.
Festfensterbegrenzer
Die AddFixedWindowLimiter-Methode verwendet ein festes Zeitfenster, um Anforderungen einzuschränken. Wenn das Zeitfenster abläuft, wird ein neues Zeitfenster gestartet, und die Anforderungsbegrenzung wird zurückgesetzt.
Betrachten Sie folgenden Code:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(_ => _
.AddFixedWindowLimiter(policyName: "fixed", options =>
{
options.PermitLimit = 4;
options.Window = TimeSpan.FromSeconds(12);
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = 2;
}));
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}"))
.RequireRateLimiting("fixed");
app.Run();
Der vorangehende Code:
- AddRateLimiter wird aufgerufen, um der Dienstsammlung einen Ratenbegrenzungsdienst hinzuzufügen.
-
AddFixedWindowLimiterwird aufgerufen, um einen festen Fensterbegrenzer mit dem Richtliniennamen"fixed"zu erstellen und setzt Folgendes fest: - PermitLimit auf 4 und Window für die Zeit auf 12. Es sind maximal 4 Anforderungen pro 12-Sekunden-Fenster zulässig.
- QueueProcessingOrder auf OldestFirst.
- QueueLimit auf 2 (legen Sie diesen auf 0 fest, um den Warteschlangenmechanismus zu deaktivieren).
- UseRateLimiter wird aufgerufen, um die Ratenbegrenzung zu aktivieren.
Apps sollten die Konfiguration verwenden, um Begrenzungsoptionen festzulegen. Der folgende Code aktualisiert den vorherigen Code mithilfe von MyRateLimitOptions für die Konfiguration:
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MyRateLimitOptions>(
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit));
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
var fixedPolicy = "fixed";
builder.Services.AddRateLimiter(_ => _
.AddFixedWindowLimiter(policyName: fixedPolicy, options =>
{
options.PermitLimit = myOptions.PermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", () => Results.Ok($"Fixed Window Limiter {GetTicks()}"))
.RequireRateLimiting(fixedPolicy);
app.Run();
UseRateLimiter muss nach UseRouting aufgerufen werden, wenn endpunktspezifische APIs für die Ratenbegrenzung verwendet werden. Wenn zum Beispiel das Attribut [EnableRateLimiting] verwendet wird, muss UseRateLimiter nach UseRouting aufgerufen werden. Wenn nur globale Begrenzungen aufgerufen werden, UseRateLimiter kann vor UseRouting aufgerufen werden.
Gleitende Fensterbegrenzung
Für einen Algorithmus mit gleitendem Zeitfenster gilt Folgendes:
- Er ähnelt der Begrenzung mit festem Zeitfenster, fügt jedoch Segmente pro Fenster hinzu. Das Fenster wird in jedem Segmentintervall immer um ein Segment verschoben. Das Segmentintervall ist (Zeitfenster)/(Segmente pro Fenster).
- Es schränkt die Anforderungen für ein Fenster auf
permitLimitAnforderungen ein. - Jedes Zeitfenster ist in
nSegmente pro Fenster unterteilt. - Anforderungen aus dem abgelaufenen Zeitsegment im vorangegangenen Zeitfenster (
nSegmente vor dem aktuellen Segment) werden dem aktuellen Segment hinzugefügt. Wir bezeichnen das am frühesten abgelaufene Zeitsegment aus dem vorherigen Fenster als abgelaufenes Segment.
Sehen Sie sich die folgende Tabelle an, die eine Begrenzung mit gleitendem Zeitfenster mit einem Fenster von 30 Sekunden, drei Segmenten pro Fenster und einem Grenzwert von 100 Anforderungen zeigt:
- Die oberste Zeile und erste Spalte zeigen das Zeitsegment an.
- Die zweite Zeile zeigt die restlichen verfügbaren Anforderungen an. Die verbleibenden Anforderungen werden folgendermaßen berechnet: verfügbare Anforderungen minus verarbeitete Anforderungen plus wiederverwendete Anforderungen.
- Anforderungen für die einzelnen Zeitangaben bewegen sich jeweils entlang der diagonalen blauen Linie.
- Ab der Zeit 30 wird die Anforderung aus dem abgelaufenen Zeitsegment wieder dem Anforderungslimit hinzugefügt, wie in den roten Linien dargestellt.
Die folgende Tabelle zeigt die Daten im vorherigen Diagramm in einem anderen Format. In der Spalte Verfügbar werden die Anforderungen angezeigt, die aus dem vorherigen Segment verfügbar sind (der Übertrag aus der vorherigen Zeile). Die erste Zeile zeigt 100 verfügbare Anforderungen an, da kein vorheriges Segment vorhanden ist.
| Zeit | Verfügbar | Verwendet | Aus abgelaufenen wiederverwendet | Übertrag |
|---|---|---|---|---|
| 0 | 100 | 20 | 0 | 80 |
| 10 | 80 | 30 | 0 | 50 |
| 20 | 50 | 40 | 0 | 10 |
| 30 | 10 | 30 | 20 | 0 |
| 40 | 0 | 10 | 30 | 20 |
| 50 | 20 | 10 | 40 | 50 |
| 60 | 50 | 35 | 30 | 45 |
Der folgende Code verwendet die Ratenbegrenzung mit gleitendem Zeitfenster:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
var slidingPolicy = "sliding";
builder.Services.AddRateLimiter(_ => _
.AddSlidingWindowLimiter(policyName: slidingPolicy, options =>
{
options.PermitLimit = myOptions.PermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.SegmentsPerWindow = myOptions.SegmentsPerWindow;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", () => Results.Ok($"Sliding Window Limiter {GetTicks()}"))
.RequireRateLimiting(slidingPolicy);
app.Run();
Begrenzung mit Tokenbucket
Die Tokenbucketbegrenzung ähnelt der Begrenzung mit gleitendem Zeitfenster. Statt jedoch die Anforderungen aus dem abgelaufenen Segment wieder hinzuzufügen, wird in jedem Auffüllungszeitraum eine feste Anzahl von Token hinzugefügt. Die in den einzelnen Segmenten hinzugefügten Token können die Anzahl verfügbarer Token nicht auf eine Zahl erhöhen, die die Tokenbucketbegrenzung übersteigt. Die folgende Tabelle zeigt eine Tokenbucketbegrenzung mit einem Grenzwert von 100 Token und einem Auffüllungszeitraum von 10 Sekunden.
| Zeit | Verfügbar | Verwendet | Hinzugefügt | Übertrag |
|---|---|---|---|---|
| 0 | 100 | 20 | 0 | 80 |
| 10 | 80 | 10 | 20 | 90 |
| 20 | 90 | 5 | 15 | 100 |
| 30 | 100 | 30 | 20 | 90 |
| 40 | 90 | 6 | 16 | 100 |
| 50 | 100 | 40 | 20 | 80 |
| 60 | 80 | 50 | 20 | 50 |
Der folgende Code verwendet die Tokenbucketbegrenzung:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
var tokenPolicy = "token";
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
builder.Services.AddRateLimiter(_ => _
.AddTokenBucketLimiter(policyName: tokenPolicy, options =>
{
options.TokenLimit = myOptions.TokenLimit;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
options.ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod);
options.TokensPerPeriod = myOptions.TokensPerPeriod;
options.AutoReplenishment = myOptions.AutoReplenishment;
}));
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", () => Results.Ok($"Token Limiter {GetTicks()}"))
.RequireRateLimiting(tokenPolicy);
app.Run();
Wenn AutoReplenishment auf truefestgelegt ist, füllt ein interner Timer die Token in jeder ReplenishmentPeriod auf. Wenn die Option auf false festgelegt ist, muss die App TryReplenish für die Begrenzung aufrufen.
Parallelitätsbegrenzung
Die Parallelitätsbegrenzung schränkt die Anzahl gleichzeitiger Anforderungen ein. Durch jede Anforderung wird die Parallelitätsbegrenzung um eins reduziert. Wenn eine Anforderung abgeschlossen ist, wird die Begrenzung um eins erhöht. Im Gegensatz zu anderen Anforderungsbegrenzungen, die die Gesamtanzahl von Anforderungen für einen angegebenen Zeitraum begrenzen, schränkt die Parallelitätsbegrenzung nur die Anzahl gleichzeitiger Anforderungen ein und begrenzt nicht die Anzahl der Anforderungen in einem bestimmten Zeitraum.
Der folgende Code verwendet die Parallelitätsbegrenzung:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
var concurrencyPolicy = "Concurrency";
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
builder.Services.AddRateLimiter(_ => _
.AddConcurrencyLimiter(policyName: concurrencyPolicy, options =>
{
options.PermitLimit = myOptions.PermitLimit;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", async () =>
{
await Task.Delay(500);
return Results.Ok($"Concurrency Limiter {GetTicks()}");
}).RequireRateLimiting(concurrencyPolicy);
app.Run();
Ratenbegrenzungspartitionen
Ratenbegrenzungspartition teilen den Datenverkehr in verschiedene "Buckets" auf, die jeweils ihre eigenen Counter zur Ratenbegrenzung erhalten. Dies ermöglicht eine präzisere Steuerung als ein einzelner globaler Zähler. Die Partition "Buckets" werden durch verschiedene Schlüssel definiert (z. B. Benutzer-ID, IP-Adresse oder API-Schlüssel).
Vorteile der Partitionierung
- Fairness: Ein Benutzer kann nicht das gesamte Ratenlimit für alle verbrauchen.
- Granularität: Unterschiedliche Grenzwerte für unterschiedliche Benutzer/Ressourcen
- Sicherheit: Besserer Schutz vor gezielten Missbrauch
- Mehrstufiger Dienst: Unterstützung für Dienstebenen mit unterschiedlichen Grenzwerten
Durch partitionierte Ratenbeschränkungen können Sie präzise steuern, wie Sie API-Datenverkehr verwalten und gleichzeitig eine faire Ressourcenzuordnung sicherstellen.
Nach IP-Adresse
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 50,
Window = TimeSpan.FromMinutes(1)
}));
Von Benutzer Identity
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.User.Identity?.Name ?? "anonymous",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
Nach API-Schlüssel
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
string apiKey = httpContext.Request.Headers["X-API-Key"].ToString() ?? "no-key";
// Different limits based on key tier
return apiKey switch
{
"premium-key" => RateLimitPartition.GetFixedWindowLimiter(
partitionKey: apiKey,
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 1000,
Window = TimeSpan.FromMinutes(1)
}),
_ => RateLimitPartition.GetFixedWindowLimiter(
partitionKey: apiKey,
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}),
};
});
Nach Endpunkt-Pfad
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
string path = httpContext.Request.Path.ToString();
// Different limits for different paths
if (path.StartsWith("/api/public"))
{
return RateLimitPartition.GetFixedWindowLimiter(
partitionKey: $"{httpContext.Connection.RemoteIpAddress}-public",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 30,
Window = TimeSpan.FromSeconds(10)
});
}
return RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
});
});
Erstellen verketteter Begrenzungen
Die CreateChained-API ermöglicht das Übergeben mehrerer PartitionedRateLimiter, die zu einem PartitionedRateLimiter kombiniert werden. Der kombinierte Begrenzer läuft alle Eingabebegrenzer nacheinander durch.
Der folgende Code verwendet CreateChained:
using System.Globalization;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(_ =>
{
_.OnRejected = async (context, cancellationToken) =>
{
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
context.HttpContext.Response.Headers.RetryAfter =
((int) retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
}
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.HttpContext.Response.WriteAsync("Too many requests. Please try again later.", cancellationToken);
};
_.GlobalLimiter = PartitionedRateLimiter.CreateChained(
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
return RateLimitPartition.GetFixedWindowLimiter
(userAgent, _ =>
new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 4,
Window = TimeSpan.FromSeconds(2)
});
}),
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
return RateLimitPartition.GetFixedWindowLimiter
(userAgent, _ =>
new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 20,
Window = TimeSpan.FromSeconds(30)
});
}));
});
var app = builder.Build();
app.UseRateLimiter();
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}"));
app.Run();
Weitere Informationen finden Sie im CreateChained-Quellcode.
Auswählen, was passiert, wenn eine Anfrage rate-limitiert ist
Für einfache Fälle können Sie einfach den Statuscode festlegen:
builder.Services.AddRateLimiter(options =>
{
// Set a custom status code for rejections
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Rate limiter configuration...
});
Der häufigste Ansatz besteht darin, einen OnRejected-Rückruf beim Konfigurieren der Ratelimitierung zu registrieren:
builder.Services.AddRateLimiter(options =>
{
// Rate limiter configuration...
options.OnRejected = async (context, cancellationToken) =>
{
// Custom rejection handling logic
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.HttpContext.Response.Headers["Retry-After"] = "60";
await context.HttpContext.Response.WriteAsync("Rate limit exceeded. Please try again later.", cancellationToken);
// Optional logging
logger.LogWarning("Rate limit exceeded for IP: {IpAddress}",
context.HttpContext.Connection.RemoteIpAddress);
};
});
Eine weitere Option besteht darin, die Anforderung in die Warteschlange zu stellen:
Warteschlange für Anfragen
Wenn die Warteschlange aktiviert ist, wird eine Anforderung, die den Satzgrenzwert überschreitet, in einer Warteschlange platziert, in der die Anforderung wartet, bis eine Genehmigung verfügbar ist oder bis ein Timeout eintritt. Anforderungen werden gemäß einer konfigurierbaren Warteschlangenreihenfolge verarbeitet.
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("api", options =>
{
options.PermitLimit = 10; // Allow 10 requests
options.Window = TimeSpan.FromSeconds(10); // Per 10-second window
options.QueueLimit = 5; // Queue up to 5 additional requests
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; // Process oldest requests first
options.AutoReplenishment = true; // Default: automatically replenish permits
});
});
EnableRateLimiting- und DisableRateLimiting-Attribute
Die Attribute [EnableRateLimiting] und [DisableRateLimiting] können auf einen Controller, eine Aktionsmethode oder eine Razor-Seite angewendet werden. Für Razor Pages muss das Attribut auf die Razor-Seite und nicht auf die Seitenhandler angewendet werden. Beispielsweise kann [EnableRateLimiting] nicht auf OnGet, OnPost oder andere Seitenhandler angewendet werden.
Das [DisableRateLimiting]-Attribut deaktiviert die Ratenbegrenzung für den Controller, die Aktionsmethode oder die Razor-Seite, unabhängig von der Anwendung benannter Ratenbegrenzungen oder globaler Begrenzungen. Betrachten Sie beispielsweise den folgenden Code, der RequireRateLimiting aufruft, um die fixedPolicy-Ratenbegrenzung auf alle Controllerendpunkte anzuwenden:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.Configure<MyRateLimitOptions>(
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit));
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
var fixedPolicy = "fixed";
builder.Services.AddRateLimiter(_ => _
.AddFixedWindowLimiter(policyName: fixedPolicy, options =>
{
options.PermitLimit = myOptions.PermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var slidingPolicy = "sliding";
builder.Services.AddRateLimiter(_ => _
.AddSlidingWindowLimiter(policyName: slidingPolicy, options =>
{
options.PermitLimit = myOptions.SlidingPermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.SegmentsPerWindow = myOptions.SegmentsPerWindow;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var app = builder.Build();
app.UseRateLimiter();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.MapRazorPages().RequireRateLimiting(slidingPolicy);
app.MapDefaultControllerRoute().RequireRateLimiting(fixedPolicy);
app.Run();
Im folgenden Code deaktiviert [DisableRateLimiting] die Ratenbegrenzung und setzt [EnableRateLimiting("fixed")] außer Kraft, das auf Home2Controller und app.MapDefaultControllerRoute().RequireRateLimiting(fixedPolicy) angewendet wird, die in Program.cs aufgerufen werden:
[EnableRateLimiting("fixed")]
public class Home2Controller : Controller
{
private readonly ILogger<Home2Controller> _logger;
public Home2Controller(ILogger<Home2Controller> logger)
{
_logger = logger;
}
public ActionResult Index()
{
return View();
}
[EnableRateLimiting("sliding")]
public ActionResult Privacy()
{
return View();
}
[DisableRateLimiting]
public ActionResult NoLimit()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Im vorherigen Code wird [EnableRateLimiting("sliding")]nicht auf die Privacy-Aktionsmethode angewendet, da Program.cs in app.MapDefaultControllerRoute().RequireRateLimiting(fixedPolicy) aufgerufen wurde.
Betrachten Sie den folgenden Code, der RequireRateLimiting nicht für MapRazorPages oder MapDefaultControllerRoute aufruft:
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using WebRateLimitAuth.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.Configure<MyRateLimitOptions>(
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit));
var myOptions = new MyRateLimitOptions();
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
var fixedPolicy = "fixed";
builder.Services.AddRateLimiter(_ => _
.AddFixedWindowLimiter(policyName: fixedPolicy, options =>
{
options.PermitLimit = myOptions.PermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var slidingPolicy = "sliding";
builder.Services.AddRateLimiter(_ => _
.AddSlidingWindowLimiter(policyName: slidingPolicy, options =>
{
options.PermitLimit = myOptions.SlidingPermitLimit;
options.Window = TimeSpan.FromSeconds(myOptions.Window);
options.SegmentsPerWindow = myOptions.SegmentsPerWindow;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = myOptions.QueueLimit;
}));
var app = builder.Build();
app.UseRateLimiter();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.MapRazorPages();
app.MapDefaultControllerRoute(); // RequireRateLimiting not called
app.Run();
Betrachten Sie den folgenden Controller:
[EnableRateLimiting("fixed")]
public class Home2Controller : Controller
{
private readonly ILogger<Home2Controller> _logger;
public Home2Controller(ILogger<Home2Controller> logger)
{
_logger = logger;
}
public ActionResult Index()
{
return View();
}
[EnableRateLimiting("sliding")]
public ActionResult Privacy()
{
return View();
}
[DisableRateLimiting]
public ActionResult NoLimit()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Im oben aufgeführten Controller gilt Folgendes:
- Die Richtlinienratenbegrenzung
"fixed"wird auf alle Aktionsmethoden angewendet, die nicht über die AttributeEnableRateLimitingundDisableRateLimitingverfügen. - Die Richtlinienratenbegrenzung
"sliding"wird auf diePrivacy-Aktion angewendet. - Die Ratenbegrenzung wird für die
NoLimit-Aktionsmethode deaktiviert.
Metriken zur Ratenbegrenzung
Die Middleware zur Begrenzung der Rate bietet integrierte Metriken und Überwachungsfunktionen , um zu verstehen, wie Sich Ratelimits auf die Leistung und Benutzererfahrung der App auswirken. Eine Liste der Metriken finden Sie unter Microsoft.AspNetCore.RateLimiting.
Testen von Endpunkten mit Ratenbegrenzung
Bevor Sie eine App mit Ratenbegrenzung in der Produktion bereitstellen, testen Sie die App, um die verwendeten Ratenbegrenzungen und -optionen zu validieren. Erstellen Sie beispielsweise ein JMeter-Skript mit einem Tool wie BlazeMeter oder Apache JMeter HTTP(S) Test Script Recorder, und laden Sie das Skript in Azure Load Testing.
Das Erstellen von Partitionen mit Benutzereingabe macht die App anfällig für Denial-of-Service-Angriffe (DoS). Durch das Erstellen von Partitionen für Client-IP-Adressen wird die App beispielsweise anfällig für Denial-of-Service-Angriffe, die IP-Quelladressen-Spoofing verwenden. Weitere Informationen finden Sie unter BCP 38 RFC 2827 Netzwerkeingangsfilterung: Abwehr von Denial-of-Service-Angriffen mit IP-Quelladressen-Spoofing.
Zusätzliche Ressourcen
- Middleware mit Bandbreitenbegrenzung von Maarten Balliauw bietet eine ausgezeichnete Einführung und Übersicht über die Rate-Limitierung.
- Ratenbegrenzung eines HTTP-Handlers in .NET