Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu makalede, gönderdiği istek sayısını sınırlayan bir istemci tarafı HTTP işleyicisi oluşturmayı öğreneceksiniz.
HttpClient'ın "www.example.com" kaynağına eriştiğini görürsünüz. Kaynaklar, bu kaynaklara dayanan uygulamalar tarafından kullanılır ve bir uygulama tek bir kaynak için çok fazla istekte bulunursa,
Hız sınırlama nedir?
Hız sınırlama, bir kaynağa ne kadar erişilebileceğini sınırlama kavramıdır. Örneğin, uygulamanızın eriştiği bir veritabanının dakikada 1.000 isteği güvenli bir şekilde işleyebileceğini biliyor olabilirsiniz, ancak bundan çok daha fazlasını işlemeyebilir. Uygulamanıza dakikada yalnızca 1.000 isteğe izin veren ve veritabanına erişebilmeleri için daha fazla isteği reddeden bir hız sınırlayıcısı koyabilirsiniz. Bu nedenle, veritabanınıza oran sınırlaması getirerek uygulamanızın güvenli sayıda isteği işlemesine olanak sağlayın. Bu, dağıtılmış sistemlerde yaygın olarak kullanılan bir desendir; burada bir uygulamanın birden çok örneği çalışıyor olabilir ve hepsinin aynı anda veritabanına erişmeye çalışmadığından emin olmak istersiniz. İstek akışını denetlemek için birden çok farklı hız sınırlama algoritması vardır.
.NET'te hız sınırlamayı kullanmak için System.Threading.RateLimiting NuGet paketine başvuracaksınız.
DelegatingHandler alt sınıfı uygulama
İstek akışını denetlemek için özel bir DelegatingHandler alt sınıfı uygularsınız. Bu, istekleri sunucuya gönderilmeden önce kesmenize ve işlemenize olanak tanıyan bir HttpMessageHandler türüdür. Yanıtları, çağırana döndürülmeden önce yakalayabilir ve işleyebilirsiniz. Bu örnekte, tek bir kaynağa gönderilebilen istek sayısını sınırlayan özel bir DelegatingHandler alt sınıfı uygulayacaksınız. Aşağıdaki özel ClientSideRateLimitedHandler sınıfını göz önünde bulundurun:
internal sealed class ClientSideRateLimitedHandler(
RateLimiter limiter)
: DelegatingHandler(new HttpClientHandler()), IAsyncDisposable
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
using RateLimitLease lease = await limiter.AcquireAsync(
permitCount: 1, cancellationToken);
if (lease.IsAcquired)
{
return await base.SendAsync(request, cancellationToken);
}
var response = new HttpResponseMessage(HttpStatusCode.TooManyRequests);
if (lease.TryGetMetadata(
MetadataName.RetryAfter, out TimeSpan retryAfter))
{
response.Headers.Add(
"Retry-After",
((int)retryAfter.TotalSeconds).ToString(
NumberFormatInfo.InvariantInfo));
}
return response;
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
await limiter.DisposeAsync().ConfigureAwait(false);
Dispose(disposing: false);
GC.SuppressFinalize(this);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
limiter.Dispose();
}
}
}
Yukarıdaki C# kodu:
-
DelegatingHandlertürünü devralır. - IAsyncDisposable arabirimini uygular.
- Oluşturucudan atanan bir
RateLimiteralanı tanımlar. - sunucuya gönderilmeden önce istekleri kesmek ve işlemek için
SendAsyncyöntemini geçersiz kılar. -
DisposeAsync() örneğini atmak için
RateLimiteryöntemini geçersiz kılar.
SendAsync yöntemine biraz daha yakından baktığınızda şunları görürsünüz:
- Bu,
RateLimiter\'den birRateLimitLeaseelde etmek içinAcquireAsyncörneğine dayanır. -
lease.IsAcquiredözelliğitrueolduğunda istek sunucuya gönderilir. - Aksi takdirde, HttpResponseMessage durum koduyla bir
429döndürülür veleaseRetryAfterbir değer içeriyorsa,Retry-Afterüst bilgisi bu değere ayarlanır.
Birçok eşzamanlı isteği simule etme
Bu özel DelegatingHandler alt sınıfını test etmek için birçok eşzamanlı isteği simüle eden bir konsol uygulaması oluşturacaksınız. Bu Program sınıfı, özel HttpClientile bir ClientSideRateLimitedHandler oluşturur:
var options = new TokenBucketRateLimiterOptions
{
TokenLimit = 8,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 3,
ReplenishmentPeriod = TimeSpan.FromMilliseconds(1),
TokensPerPeriod = 2,
AutoReplenishment = true
};
// Create an HTTP client with the client-side rate limited handler.
using HttpClient client = new(
handler: new ClientSideRateLimitedHandler(
limiter: new TokenBucketRateLimiter(options)));
// Create 100 urls with a unique query string.
var oneHundredUrls = Enumerable.Range(0, 100).Select(
i => $"https://example.com?iteration={i:0#}");
// Flood the HTTP client with requests.
var floodOneThroughFortyNineTask = Parallel.ForEachAsync(
source: oneHundredUrls.Take(0..49),
body: (url, cancellationToken) => GetAsync(client, url, cancellationToken));
var floodFiftyThroughOneHundredTask = Parallel.ForEachAsync(
source: oneHundredUrls.Take(^50..),
body: (url, cancellationToken) => GetAsync(client, url, cancellationToken));
await Task.WhenAll(
floodOneThroughFortyNineTask,
floodFiftyThroughOneHundredTask);
static async ValueTask GetAsync(
HttpClient client, string url, CancellationToken cancellationToken)
{
using var response =
await client.GetAsync(url, cancellationToken);
Console.WriteLine(
$"URL: {url}, HTTP status code: {response.StatusCode} ({(int)response.StatusCode})");
}
Önceki konsol uygulamasında:
-
TokenBucketRateLimiterOptions,8belirteç sınırı veOldestFirstkuyruk işleme sırası,3kuyruk sınırı ve1milisaniyelik stok yenileme dönemi,2dönem başına belirteçler vetrueotomatik yenileme değeri ile yapılandırılır. -
HttpClientile yapılandırılanClientSideRateLimitedHandlerile birTokenBucketRateLimiteroluşturulur. - 100 isteği taklit etmek için Enumerable.Range, her birinde benzersiz bir sorgu dizesi parametresi olan 100 URL oluşturur.
- Task yönteminden iki Parallel.ForEachAsync nesnesi atanır ve URL'ler iki gruba bölünür.
-
HttpClienther URL'ye birGETisteği göndermek için kullanılır ve yanıt konsola yazılır. - Task.WhenAll her iki görevin tamamlanmasını bekler.
HttpClient
ClientSideRateLimitedHandlerile yapılandırıldığından, tüm istekler sunucu kaynağına ulaşamayabilir. Konsol uygulamasını çalıştırarak bu onaylama işlemini test edebilirsiniz. Sunucuya toplam istek sayısının yalnızca bir kısmının gönderildiğini ve geri kalanının 429HTTP durum koduyla reddedildiğini göreceksiniz. Sunucuya gönderilen istek sayısının nasıl değiştiğini görmek için options oluşturmak için kullanılan TokenBucketRateLimiter nesnesini değiştirmeyi deneyin.
Aşağıdaki örnek çıkışı göz önünde bulundurun:
URL: https://example.com?iteration=06, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=60, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=55, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=59, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=57, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=11, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=63, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=13, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=62, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=65, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=64, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=67, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=14, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=68, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=16, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=69, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=70, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=71, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=17, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=18, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=72, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=73, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=74, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=19, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=75, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=76, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=79, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=77, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=21, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=78, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=81, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=22, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=80, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=20, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=82, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=83, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=23, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=84, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=24, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=85, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=86, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=25, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=87, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=26, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=88, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=89, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=27, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=90, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=28, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=91, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=94, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=29, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=93, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=96, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=92, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=95, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=31, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=30, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=97, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=98, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=99, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=32, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=33, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=34, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=35, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=36, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=37, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=38, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=39, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=40, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=41, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=42, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=43, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=44, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=45, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=46, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=47, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=48, HTTP status code: TooManyRequests (429)
URL: https://example.com?iteration=15, HTTP status code: OK (200)
URL: https://example.com?iteration=04, HTTP status code: OK (200)
URL: https://example.com?iteration=54, HTTP status code: OK (200)
URL: https://example.com?iteration=08, HTTP status code: OK (200)
URL: https://example.com?iteration=00, HTTP status code: OK (200)
URL: https://example.com?iteration=51, HTTP status code: OK (200)
URL: https://example.com?iteration=10, HTTP status code: OK (200)
URL: https://example.com?iteration=66, HTTP status code: OK (200)
URL: https://example.com?iteration=56, HTTP status code: OK (200)
URL: https://example.com?iteration=52, HTTP status code: OK (200)
URL: https://example.com?iteration=12, HTTP status code: OK (200)
URL: https://example.com?iteration=53, HTTP status code: OK (200)
URL: https://example.com?iteration=07, HTTP status code: OK (200)
URL: https://example.com?iteration=02, HTTP status code: OK (200)
URL: https://example.com?iteration=01, HTTP status code: OK (200)
URL: https://example.com?iteration=61, HTTP status code: OK (200)
URL: https://example.com?iteration=05, HTTP status code: OK (200)
URL: https://example.com?iteration=09, HTTP status code: OK (200)
URL: https://example.com?iteration=03, HTTP status code: OK (200)
URL: https://example.com?iteration=58, HTTP status code: OK (200)
URL: https://example.com?iteration=50, HTTP status code: OK (200)
İlk günlüğe kaydedilen girişlerin her zaman hemen döndürülen 429 yanıt olduğunu ve son girişlerin her zaman 200 yanıt olduğunu fark edeceksiniz. Bunun nedeni hız sınırıyla istemci tarafında karşılaşılması ve bir sunucuya HTTP çağrısı yapmaktan kaçınmasıdır. Sunucunun isteklerle dolu olmadığı anlamına geldiği için bu iyi bir şeydir. Bu, oran sınırının tüm istemciler arasında tutarlı bir şekilde uygulandığını da gösterir.
Ayrıca her URL'nin sorgu dizesinin benzersiz olduğunu unutmayın: her istek için bir artırıldığını görmek için iteration parametresini inceleyin. Bu parametre, 429 yanıtlarının ilk isteklerden değil, hız sınırına ulaşıldıktan sonra yapılan isteklerden geldiğini göstermeye yardımcı olur. 200 yanıt daha sonra gelir, ancak bu istekler sınıra ulaşılamadan önce yapılmıştır.
Çeşitli hız sınırlama algoritmalarını daha iyi anlamak için farklı bir RateLimiter uygulamasını kabul etmek için bu kodu yeniden yazmayı deneyin. TokenBucketRateLimiter ek olarak aşağıdakileri deneyebilirsiniz:
Özet
Bu makalede, özel bir ClientSideRateLimitedHandler'ı nasıl uygulayacağınızı öğrendiniz. Bu desen, API sınırları olduğunu bildiğiniz kaynaklar için hız sınırlı bir HTTP istemcisi uygulamak için kullanılabilir. Bu şekilde, istemci uygulamanızın sunucuya gereksiz isteklerde bulunmasını engeller ve uygulamanızın sunucu tarafından engellenmesini de engellersiniz. Ayrıca, yeniden deneme zamanlaması değerlerini depolamak için meta verilerin kullanılmasıyla, otomatik yeniden deneme mantığını da uygulayabilirsiniz.
Ayrıca bkz.
- .NET için Hız Sınırlandırma duyurusu
- ASP.NET Core'da
Hız sınırlama ara yazılımı - Azure Mimarisini : Hız sınırlama düzeni
- .NET'te otomatik yeniden deneme mantığı