Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Oleh Arvin Kahbazi, Maarten Balliauw, dan Rick Anderson
Middleware Microsoft.AspNetCore.RateLimiting menyediakan middleware untuk pembatasan laju. Aplikasi mengonfigurasi kebijakan pembatasan tarif lalu melampirkan kebijakan ke titik akhir. Aplikasi yang menggunakan pembatasan tarif harus diuji dan ditinjau dengan cermat sebelum disebarkan. Lihat Menguji endpoint dengan pembatasan kecepatan dalam artikel ini untuk informasi lebih lanjut.
Untuk pengenalan pembatasan laju, lihat Middleware pembatasan laju.
Mengapa menggunakan pembatasan tarif
Pembatasan tarif dapat digunakan untuk mengelola alur permintaan masuk ke aplikasi. Alasan utama untuk menerapkan pembatasan tarif:
- Mencegah Penyalahgunaan: Pembatasan tarif membantu melindungi aplikasi dari penyalahgunaan dengan membatasi jumlah permintaan yang dapat dibuat pengguna atau klien dalam periode waktu tertentu. Ini sangat penting untuk API publik.
- Memastikan Penggunaan yang Adil: Dengan menetapkan batas, semua pengguna memiliki akses yang adil ke sumber daya, mencegah pengguna memonopoli sistem.
- Melindungi Sumber Daya: Pembatasan laju membantu mencegah kelebihan beban server dengan mengontrol jumlah permintaan yang dapat diproses, sehingga melindungi sumber daya backend dari kewalahan.
- Meningkatkan keamanan: Ini dapat mengurangi risiko serangan Denial of Service (DoS) dengan membatasi tingkat di mana permintaan diproses, sehingga lebih sulit bagi penyerang untuk membanjiri sistem.
- Meningkatkan Performa: Dengan mengontrol tingkat permintaan masuk, performa dan responsivitas aplikasi yang optimal dapat dipertahankan, memastikan pengalaman pengguna yang lebih baik.
- Cost Management: Untuk layanan yang dikenakan biaya berdasarkan penggunaan, pembatasan tarif dapat membantu mengelola dan memprediksi pengeluaran dengan mengontrol volume permintaan yang diproses.
Menerapkan pembatasan tarif dalam aplikasi ASP.NET Core dapat membantu menjaga stabilitas, keamanan, dan performa, memastikan layanan yang andal dan efisien untuk semua pengguna.
Mencegah Serangan DDoS
Meskipun pembatasan tarif dapat membantu mengurangi risiko serangan Denial of Service (DoS) dengan membatasi tingkat di mana permintaan diproses, ini bukan solusi komprehensif untuk serangan Distributed Denial of Service (DDoS). Serangan DDoS melibatkan beberapa sistem yang membanjiri aplikasi dengan banjir permintaan, sehingga sulit ditangani dengan pembatasan laju saja.
Untuk perlindungan DDoS yang kuat, pertimbangkan untuk menggunakan layanan perlindungan DDoS komersial. Layanan ini menawarkan fitur lanjutan seperti:
- Analisis Lalu Lintas: Pemantauan dan analisis berkelanjutan lalu lintas masuk untuk mendeteksi dan mengurangi serangan DDoS secara real time.
- Skalabilitas: Kemampuan untuk menangani serangan skala besar dengan mendistribusikan lalu lintas di beberapa server dan pusat data.
- Mitigasi otomatis: Mekanisme respons otomatis untuk memblokir lalu lintas berbahaya dengan cepat tanpa intervensi manual.
- Global Network: Jaringan global server untuk menyerap dan mengurangi serangan lebih dekat ke sumbernya.
- Pembaruan Konstan: Layanan komersial terus melacak dan memperbarui mekanisme perlindungan mereka untuk beradaptasi dengan ancaman baru dan berkembang.
Saat menggunakan layanan hosting cloud, perlindungan DDoS biasanya tersedia sebagai bagian dari solusi hosting, seperti Azure Web Application Firewall, AWS Shield atau Google Cloud Armor. Perlindungan khusus tersedia sebagai Web Application Firewalls (WAF) atau sebagai bagian dari solusi CDN seperti Cloudflare atau Akamai Kona Site Defender
Menerapkan layanan perlindungan DDoS komersial bersama dengan pembatasan tarif dapat memberikan strategi pertahanan yang komprehensif, memastikan stabilitas, keamanan, dan performa aplikasi.
Gunakan Middleware Pembatasan Laju
Langkah-langkah berikut menunjukkan cara menggunakan middleware pembatasan laju di aplikasi ASP.NET Core:
- Mengonfigurasi layanan pembatasan tarif.
Dalam file Program.cs, konfigurasikan layanan pembatasan tarif dengan menambahkan kebijakan pembatasan tarif yang sesuai. Kebijakan dapat didefinisikan sebagai kebijakan global atau kebijakan bernama. Contoh berikut mengizinkan 10 permintaan per menit berdasarkan pengguna (identitas) atau secara global:
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)
}));
});
Kebijakan yang diberi nama perlu diterapkan secara eksplisit pada halaman atau titik akhir. Contoh berikut menambahkan kebijakan pembatas jendela tetap bernama "fixed" yang akan kita tambahkan ke titik akhir nanti:
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();
Pembatas global berlaku untuk semua titik akhir secara otomatis saat dikonfigurasi melalui opsi . GlobalLimiter.
Aktifkan middleware pembatasan kecepatan
Dalam file
Program.cs, aktifkan middleware pembatasan tarif dengan memanggil UseRateLimiter:
app.UseRouting();
app.UseRateLimiter();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
Menerapkan kebijakan pembatasan tarif ke titik akhir atau halaman
Menerapkan pembatasan tarif ke Titik Akhir WebAPI
Terapkan kebijakan bernama ke titik akhir atau grup, misalnya:
app.MapGet("/api/resource", () => "This endpoint is rate limited")
.RequireRateLimiting("fixed"); // Apply specific policy to an endpoint
Menerapkan pembatasan laju pada Pengontrol MVC
Terapkan kebijakan pembatasan tarif yang dikonfigurasi ke titik akhir tertentu atau secara global. Misalnya, untuk menerapkan kebijakan "tetap" ke semua titik akhir pengontrol:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireRateLimiting("fixed");
});
Menerapkan pembatasan tarif ke aplikasi Blazor sisi server
Untuk menetapkan pembatasan tarif untuk semua komponen Razor yang dapat dirutekan aplikasi, tentukan RequireRateLimiting dengan nama kebijakan pembatasan tarif pada panggilan MapRazorComponents dalam file Program. Dalam contoh berikut, kebijakan pembatasan tarif bernama "policy" diterapkan:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.RequireRateLimiting("policy");
Untuk menetapkan kebijakan untuk sebuah komponen Razor yang dapat dirutekan atau folder berisi komponen melalui sebuah file _Imports.razor, atribut [EnableRateLimiting] diterapkan dengan nama kebijakan. Dalam contoh berikut, kebijakan pembatasan tarif bernama "override" diterapkan. Kebijakan ini menggantikan semua kebijakan yang saat ini diterapkan pada endpoint. Pembatas global tetap berfungsi pada titik akhir dengan penerapan atribut ini.
@page "/counter"
@using Microsoft.AspNetCore.RateLimiting
@attribute [EnableRateLimiting("override")]
<h1>Counter</h1>
Atribut [EnableRateLimiting] hanya diterapkan ke komponen yang dapat dirutekan atau folder komponen melalui file _Imports.razor jika RequireRateLimitingtidak dipanggil pada MapRazorComponents.
Atribut [DisableRateLimiting] digunakan untuk menonaktifkan pembatasan laju untuk komponen yang dapat dirutekan atau folder komponen melalui file _Imports.razor.
Algoritma pembatas laju
Kelas RateLimiterOptionsExtensions ini menyediakan metode ekstensi berikut untuk pembatasan tarif:
Pembatas tetap, geser, dan token semuanya membatasi jumlah maksimum permintaan dalam periode waktu tertentu. Pembatas konkurensi hanya membatasi jumlah permintaan bersamaan dan tidak membatasi jumlah permintaan dalam jangka waktu tertentu. Biaya titik akhir harus dipertimbangkan saat memilih pembatas. Biaya titik akhir mencakup sumber daya yang digunakan, misalnya, waktu, akses data, CPU, dan I/O.
Pembatas jendela tetap
Metode ini AddFixedWindowLimiter menggunakan jendela waktu tetap untuk membatasi permintaan. Saat jendela waktu kedaluwarsa, jendela waktu baru dimulai dan batas permintaan diatur ulang.
Pertimbangkan kode berikut:
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();
Kode sebelumnya:
- Panggilan untuk menambahkan layanan pembatasan laju ke himpunan layanan AddRateLimiter.
- Memanggil
AddFixedWindowLimiteruntuk membuat pembatas jendela tetap dengan nama kebijakan"fixed"dan mengatur: - PermitLimit ke 4 dan jam Window ke 12. Maksimum 4 permintaan per setiap jendela 12 detik diizinkan.
- QueueProcessingOrder ke OldestFirst.
- QueueLimit ke 2 (atur ini ke 0 untuk menonaktifkan mekanisme antrean).
- Memanggil UseRateLimiter untuk mengaktifkan pembatasan tarif.
Aplikasi harus menggunakan Konfigurasi untuk mengatur opsi pembatas. Kode berikut memperbarui kode sebelumnya menggunakan MyRateLimitOptions untuk konfigurasi:
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 harus dipanggil setelah UseRouting ketika pembatasan kecepatan API titik akhir tertentu digunakan. Misalnya, jika [EnableRateLimiting] atribut digunakan, UseRateLimiter harus dipanggil setelah UseRouting. Saat hanya memanggil pembatas global, UseRateLimiter dapat dipanggil sebelum UseRouting.
Pengatur jendela geser
Algoritma dengan jendela geser
- Mirip dengan pembatas jendela tetap tetapi menambahkan segmen per jendela. Jendela menggeser satu segmen setiap selang segmen. Interval segmen adalah (waktu jendela)/(segmen per jendela).
- Membatasi jumlah permintaan jendela ke
permitLimitpermintaan. - Setiap jendela waktu dibagi dalam
nsegmen per jendela. - Permintaan yang diambil dari segmen waktu yang sudah kedaluwarsa pada satu jendela sebelumnya (
nsegmen sebelum segmen saat ini) ditambahkan ke segmen saat ini. Kami merujuk ke segmen waktu yang paling kedaluwarsa dari satu jendela sebelumnya sebagai segmen yang kedaluwarsa.
Pertimbangkan tabel berikut yang memperlihatkan pembatas jendela geser dengan jendela 30 detik, tiga segmen per jendela, dan batas 100 permintaan:
- Baris atas dan kolom pertama memperlihatkan segmen waktu.
- Baris kedua memperlihatkan permintaan yang masih tersedia. Permintaan yang tersisa dihitung sebagai permintaan yang tersedia dikurangi permintaan yang diproses ditambah permintaan daur ulang.
- Permintaan pada setiap waktu bergerak di sepanjang garis biru diagonal.
- Mulai dari waktu 30, permintaan dari segmen waktu yang sudah kedaluwarsa ditambahkan kembali ke dalam batas permintaan, seperti yang ditunjukkan pada garis merah.
Tabel berikut ini memperlihatkan data dalam grafik sebelumnya dalam format yang berbeda. Kolom Tersedia memperlihatkan permintaan yang tersedia dari segmen sebelumnya (Dibawa dari baris sebelumnya). Baris pertama menunjukkan 100 permintaan yang tersedia karena belum ada segmen sebelumnya.
| Waktu | Tersedia | Diambil | Didaur ulang dari produk yang sudah kedaluwarsa | Melanjutkan |
|---|---|---|---|---|
| 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 |
Kode berikut menggunakan pembatas laju jendela geser:
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();
Pembatas wadah token
Pembatas wadah token mirip dengan pembatas jendela geser, tetapi daripada menambahkan kembali permintaan yang diambil dari segmen yang kedaluwarsa, jumlah token tetap ditambahkan setiap periode pengisian ulang. Token yang ditambahkan setiap segmen tidak dapat meningkatkan token yang tersedia ke angka yang lebih tinggi dari batas wadah token. Tabel berikut menunjukkan pembatas wadah token dengan batas 100 token dan periode pengisian ulang 10 detik.
| Waktu | Tersedia | Diambil | Ditambahkan | Melanjutkan |
|---|---|---|---|---|
| 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 |
Kode berikut menggunakan pembatas wadah token:
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();
Ketika AutoReplenishment diatur ke true, timer internal mengisi ulang token setiap ReplenishmentPeriod; ketika diatur ke false, aplikasi harus memanggil TryReplenish pembatas.
Pembatas konkurensi
Pembatas konkurensi membatasi jumlah permintaan bersamaan. Setiap permintaan mengurangi batas konkurensi satu per satu. Ketika permintaan selesai, batas ditingkatkan sebanyak satu. Tidak seperti pembatas permintaan lain yang membatasi jumlah total permintaan untuk periode tertentu, pembatas konkurensi hanya membatasi jumlah permintaan bersamaan dan tidak membatasi jumlah permintaan dalam jangka waktu tertentu.
Kode berikut menggunakan pembatas konkurensi:
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();
Partisi Pembatasan Kecepatan
Partisi pembatasan laju membagi lalu lintas menjadi "ember" terpisah yang masing-masing mendapatkan penghitung batas laju mereka sendiri. Ini memungkinkan kontrol yang lebih terperinci daripada satu penghitung global. "Bagian" partisi didefinisikan oleh kunci yang berbeda (seperti ID pengguna, alamat IP, atau kunci API).
Manfaat Pemartisian
- Fairness: Satu pengguna tidak dapat menggunakan seluruh batas tarif untuk semua orang
- Granularitas: Batas yang berbeda untuk pengguna/sumber daya yang berbeda
- Keamanan : Perlindungan yang lebih baik terhadap penyalahgunaan yang ditargetkan
- Layanan Berjenjang : Dukungan untuk tingkat layanan dengan batas yang berbeda
Pembatasan tarif yang dipartisi memberi Anda kontrol mendetail atas cara Anda mengelola lalu lintas API sambil memastikan alokasi sumber daya yang adil.
Menurut Alamat IP
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 50,
Window = TimeSpan.FromMinutes(1)
}));
Oleh Pengguna 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)
}));
Berdasarkan Kunci API
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)
}),
};
});
Menurut Jalur Titik Akhir
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)
});
});
Membuat pembatas berantai
API CreateChained memungkinkan penerusan beberapa PartitionedRateLimiter yang dikombinasikan menjadi satu PartitionedRateLimiter. Pembatas gabungan menjalankan semua pembatas input secara berurutan.
Kode berikut menggunakan 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();
Untuk informasi selengkapnya, lihat kode sumber CreateChained
Memilih apa yang terjadi ketika permintaan dibatasi oleh batas kecepatan
Untuk kasus sederhana, Anda hanya dapat mengatur kode status:
builder.Services.AddRateLimiter(options =>
{
// Set a custom status code for rejections
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Rate limiter configuration...
});
Pendekatan yang paling umum adalah mendaftarkan panggilan balik OnRejected saat mengonfigurasi pembatasan tarif:
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);
};
});
Opsi lain adalah mengantrekan permintaan:
Permintaan Antrean
Dengan antrean diaktifkan, ketika permintaan melebihi batas laju, permintaan ditempatkan dalam antrean di mana permintaan menunggu hingga izin tersedia atau sampai waktu tunggu habis. Permintaan diproses sesuai dengan urutan antrean yang dapat dikonfigurasi.
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 dan atribut DisableRateLimiting
Atribut [EnableRateLimiting] dan [DisableRateLimiting] dapat diterapkan ke Pengontrol, metode tindakan, atau Razor Halaman. Untuk Razor Halaman, atribut harus diterapkan ke Razor Halaman dan bukan penanganan halaman. Misalnya, [EnableRateLimiting] tidak dapat diterapkan ke OnGet, , OnPostatau handler halaman lainnya.
Atribut [DisableRateLimiting]menonaktifkan pembatasan tingkat ke Controller, metode aksi, atau Razor Halaman, terlepas dari pembatas laju bernama atau pembatas global yang diterapkan. Misalnya, pertimbangkan kode berikut yang memanggil RequireRateLimiting untuk menerapkan fixedPolicy pembatasan laju ke semua endpoint pengontrol.
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();
Dalam kode berikut, [DisableRateLimiting] menonaktifkan pembatasan tarif dan menimpa [EnableRateLimiting("fixed")] yang diterapkan ke Home2Controller dan dipanggil dalam app.MapDefaultControllerRoute().RequireRateLimiting(fixedPolicy) pada Program.cs.
[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 });
}
}
Dalam kode sebelumnya, [EnableRateLimiting("sliding")]tidak diterapkan ke metode aksi Privacy karena Program.cs dipanggil app.MapDefaultControllerRoute().RequireRateLimiting(fixedPolicy).
Pertimbangkan kode berikut yang tidak memanggil RequireRateLimiting di MapRazorPages atau MapDefaultControllerRoute:
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();
Pertimbangkan pengontrol berikut:
[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 });
}
}
Di pengontrol sebelumnya:
- Pembatas tingkat kebijakan
"fixed"diterapkan ke semua metode tindakan yang tidak memiliki atributEnableRateLimitingdanDisableRateLimiting. - Pembatas tarif kebijakan
"sliding"diterapkan pada tindakanPrivacy. - Pembatasan laju dinonaktifkan pada
NoLimitmetode aksi.
Metrik pembatasan laju
Middleware pembatasan laju menyediakan metrik bawaan dan kemampuan pemantauan untuk membantu memahami bagaimana batas laju memengaruhi performa aplikasi dan pengalaman pengguna. Lihat Microsoft.AspNetCore.RateLimiting untuk daftar metrik.
Menguji titik akhir dengan pembatasan kecepatan
Sebelum menyebarkan aplikasi ke dalam produksi menggunakan pembatas laju, lakukan uji tekanan pada aplikasi untuk memvalidasi pembatas laju dan opsi yang digunakan. Misalnya, buat skrip JMeter dengan alat seperti BlazeMeter atau Apache JMeter HTTP(S) Test Script Recorder dan muat skrip ke Azure Load Testing.
Membuat partisi dengan input pengguna membuat aplikasi rentan terhadap Serangan Denial of Service (DoS). Misalnya, membuat partisi pada alamat IP klien membuat aplikasi rentan terhadap Serangan Penolakan Layanan dengan menggunakan spoofing alamat sumber IP. Untuk informasi selengkapnya, lihat Pemfilteran Masuk Jaringan BCP 38 RFC 2827: Mengalahkan Serangan Penolakan Layanan yang menggunakan pemalsuan alamat sumber IP.
Sumber Daya Tambahan:
- Middleware pembatasan laju oleh Maarten Balliauw memberikan pengenalan dan ikhtisar yang sangat baik untuk pembatasan laju.
- Batasi laju handler HTTP di .NET
ASP.NET Core