• 16.08.2026 04:19:20
  • Admin Admin

ASP.NET Core eğitimi kapsamında, istek sınırlarını IP yerine kiracı ve kimlik bağlamında uygulamayı; middleware sırasını, ölçümü ve çoklu instance senaryolarını üretim odaklı ele alıyoruz.

ASP.NET Core Eğitimi: Üretimde Rate Limiting ve Yük İzolasyonu

ASP.NET Core eğitimi ile doğru partition anahtarını seçmek

Yerleşik Microsoft.AspNetCore.RateLimiting bileşeni, her uygulama instance'ında çalışan bir limiter üretir. API anahtarı veya JWT taşıyan bir SaaS uç noktasında partition anahtarını yalnızca IP adresi yapmak, aynı NAT arkasındaki yüzlerce kullanıcıyı tek kovada toplar; ayrıca yanlış yapılandırılmış proxy zincirinde X-Forwarded-For başlığı istemci tarafından taklit edilebilir. Önce UseForwardedHeaders() ile yalnızca güvenilen proxy'leri tanımlayın, sonra doğrulanmış tenant_id claim'ini kullanın.

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.10"));
});

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

    options.AddPolicy("tenant-write", context =>
    {
        var tenant = context.User.FindFirst("tenant_id")?.Value;
        var key = tenant ?? $"anonymous:{context.Connection.RemoteIpAddress}";

        return RateLimitPartition.GetTokenBucketLimiter(key, _ =>
            new TokenBucketRateLimiterOptions
            {
                TokenLimit = 30,
                TokensPerPeriod = 10,
                ReplenishmentPeriod = TimeSpan.FromSeconds(1),
                QueueLimit = 0,
                AutoReplenishment = true
            });
    });
});

app.UseForwardedHeaders();
app.UseRateLimiter();

QueueLimit = 0 bilinçli bir tercihtir: yazma uç noktasında kuyruk, bekleyen HTTP isteklerinin socket, request body ve çalışma zamanı belleğini tutmasına neden olur. Örnekte kiracı saniyede 10 token kazanır ve en fazla 30 token biriktirir; yani kısa süreli burst kabul edilirken sürekli 40 istek/saniye gönderen kiracı yaklaşık 10 istek/saniyeye düşer. Bu değerleri tahminle değil, her operasyonun downstream maliyetini ölçerek belirleyin: örneğin bir sipariş yazma isteği ortalama 2 SQL komutu çalıştırıyorsa, veritabanı başına izin verilen yazma komutu bütçesinden türetin.

.NET Core kursu projelerinde middleware sırası ve endpoint politikaları

Limiter'ı UseAuthentication() sonrasına, fakat controller veya minimal API handler'larından önce koyun; aksi halde tenant_id henüz oluşmadığı için bütün kimlikli trafik anonim partition'a düşer. RequireRateLimiting ile yalnızca maliyetli yazma yollarını hedeflemek, health check ve OIDC callback gibi yolların yanlışlıkla 429 dönmesini engeller.

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();

app.MapPost("/api/orders", CreateOrder)
   .RequireAuthorization("orders.write")
   .RequireRateLimiting("tenant-write");

app.MapGet("/healthz", () => Results.Ok())
   .DisableRateLimiting();

Reddetme yanıtına Retry-After ekleyin; istemci tarafındaki exponential backoff için makinece okunabilir bir sinyaldir. Yerleşik limiter reddetme bilgisini OnRejected içinde verir. Token bucket'ın sonraki yenilenme zamanını hesaplayamıyorsanız uydurma bir saniye değeri yazmayın; bunun yerine uygulamanızın açıkça desteklediği sabit bir geri deneme penceresi kullanın. Bu yaklaşım, csharp eğitimi veya c# eğitimi sırasında sık görülen “429 döndüm, yeterli” uygulamasından daha güvenilir bir istemci sözleşmesi oluşturur.

options.OnRejected = async (context, token) =>
{
    context.HttpContext.Response.Headers.RetryAfter = "1";
    await context.HttpContext.Response.WriteAsJsonAsync(new
    {
        error = "rate_limit_exceeded",
        traceId = Activity.Current?.TraceId.ToString()
    }, cancellationToken: token);
};

C# kursu için rate limiting ölçümü: önce-sonra testi ve profil

Limiter eklemek bir performans sonucu değildir; aynı yük profiliyle iki koşulu karşılaştırın. Önce limiter kapalıyken, sonra aynı token limitleriyle k6 çalıştırın; p95/p99 gecikme, HTTP 429 oranı, SQL komut sayısı ve CPU'yu aynı zaman aralığında kaydedin. Amaç toplam 2xx sayısını yapay biçimde yükseltmek değil, veritabanı doygunluğunda p99'un sınırsız büyümesini ve connection pool beklemelerini sınırlamaktır.

// load.js
import http from 'k6/http';
import { check } from 'k6';

export const options = {
  scenarios: {
    writes: { executor: 'constant-arrival-rate', rate: 80,
      timeUnit: '1s', duration: '2m', preAllocatedVUs: 40 }
  }
};

export default function () {
  const r = http.post(`${__ENV.BASE_URL}/api/orders`,
    JSON.stringify({ sku: 'A-42', quantity: 1 }),
    { headers: { 'Content-Type': 'application/json',
                 Authorization: `Bearer ${__ENV.TOKEN}` } });
  check(r, { '2xx or 429': x => x.status === 201 || x.status === 429 });
}

Test sırasında ayrı bir terminalde dotnet-counters monitor --process-id <pid> System.Runtime Microsoft.AspNetCore.Hosting çalıştırın; GC heap, allocation rate, thread-pool queue length ve request rate'i kaydedin. Daha derin CPU veya lock analizi için dotnet-trace collect --process-id <pid> --providers Microsoft-DotNETCore-SampleProfiler ile trace toplayıp PerfView ya da speedscope'ta inceleyin. QueueLimit'i 100'den 0'a indirdiğiniz bir karşılaştırmada, 429 sayısı artarken thread-pool queue length ve p99 düşüyorsa bu beklenen bir trade-off'tur; 429'ları başarı metriğine dahil etmeyin.

Uygulama metriğini de ekleyin; yalnızca web sunucusunun toplam 429 sayısı, hangi tenant'ın baskı oluşturduğunu göstermez. OpenTelemetry için System.Diagnostics.Metrics ile rate_limit_rejections counter'ını policy ve endpoint etiketiyle yayınlayın; tenant ID'yi metric etiketi yapmayın. Sınırsız tenant cardinality'si Prometheus bellek kullanımını ve sorgu maliyetini büyütür; tenant bilgisini yapılandırılmış log veya trace attribute olarak, örnekleme kurallarıyla taşıyın.

Çoklu instance'ta global kota: yerel limiterın sınırı

Kubernetes üzerinde dört pod varsa, yukarıdaki 10 token/saniye ayarı teorik olarak kiracı başına yaklaşık 40 token/saniyeye çıkar; çünkü PartitionedRateLimiter state'i process belleğindedir. Bu, pod sayısı arttığında fark edilmeden genişleyen bir kota hatasıdır. Global abonelik kotası gerekiyorsa Redis gibi paylaşımlı depoda atomik sayım yapın; yalnızca pod içi CPU ve connection pool koruması gerekiyorsa yerel limiter daha düşük gecikmeli seçenektir.

-- Redis EVAL: sabit pencerede atomik kota
local current = redis.call('INCR', KEYS[1])
if current == 1 then
  redis.call('EXPIRE', KEYS[1], ARGV[1])
end
if current > tonumber(ARGV[2]) then
  return {0, redis.call('TTL', KEYS[1])}
end
return {1, redis.call('TTL', KEYS[1])}

Bu Lua betiğini StackExchange.Redis üzerinden tenant:{id}:orders:{unixWindow} anahtarıyla çağırabilirsiniz. Anahtardaki {id} hash tag'i Redis Cluster'da tek slot garantisi verir. Ancak sabit pencere sınırında pencere sonu ve başlangıcında çift burst oluşur; katı “son 60 saniye” sözleşmesi için sorted-set sliding window veya GCRA tabanlı bir algoritma gerekir. Redis erişilemez olduğunda yazma uç noktası için fail-open seçimi, saldırı anında veritabanı korumasını kaldırır; ödeme ya da stok rezervasyonu gibi maliyetli işlemlerde kısa bir circuit breaker sonrası fail-closed/503 politikası daha açık bir arıza davranışıdır.

Entity Framework eğitimi ve Blazor eğitimi bağlamında kritik ayrıntılar

Rate limiter, Entity Framework sorgusunun içindeki maliyeti azaltmaz; handler'a girmeden isteği keserek DbContext, connection pool ve transaction oluşturulmasını önler. Bu nedenle entity framework eğitimi örneklerinde limiter'ı endpointten sonra çağırmak yanlıştır. Yazma handler'ında ayrıca CancellationToken'ı EF Core'a aktarın; istemci bağlantısı koptuğunda gereksiz sorgunun tamamlanmasını önlersiniz: await db.SaveChangesAsync(httpContext.RequestAborted).

Blazor Server'da HTTP rate limiting, ilk SignalR negotiate/bağlantı isteğini sınırlayabilir; Hub metot çağrıları ise ayrı bir mesaj akışıdır. Kullanıcı başına pahalı hub çağrılarını sınırlamak için IHubFilter içinde kullanıcı claim'iyle partition edilmiş bir limiter kiralayın ve InvokeMethodAsync öncesinde AcquireAsync çağırın. Bu ayrım, blazor eğitimi materyallerinde sık atlanan bir noktadır: HTTP middleware'e güvenip örneğin Search hub metodunun saniyede binlerce kez çağrılmasını engellemiş olmazsınız.

Bu konu, microsoft teknolojileri eğitimi içindeki ASP.NET Core, Redis, OpenTelemetry ve EF Core sınırlarının birlikte ele alınmasını gerektirir. Bir .net core eğitimi ya da .net core kursu laboratuvarında aynı k6 senaryosunu tek pod ve dört pod üzerinde çalıştırın; csharp kursu ve c# kursu katılımcılarından Redis arızasında 503/429 oranını, SQL connection pool beklemelerini ve trace'lerdeki reddedilme noktasını raporlamalarını isteyin. Böylece politika değerleri ezber yerine gözlenebilir kapasite bütçesine bağlanır.

İlgili Eğitim

.NET Core Eğitimi

Sık Sorulan Sorular

ASP.NET Core eğitimi sırasında rate limiter IP ile mi tenant claim'i ile mi partition edilmeli?

Kimlik doğrulanmış çok kiracılı API'lerde önce doğrulanmış tenant claim'i kullanın; IP'yi yalnızca anonim trafik için fallback yapın. Reverse proxy arkasındaysanız UseForwardedHeaders'ı UseRateLimiter'dan önce koyun ve KnownProxies/KnownNetworks tanımlayın. Aksi halde istemci X-Forwarded-For ile partition anahtarını değiştirebilir.

.NET Core kursu projesinde rate limiting ekledikten sonra hangi araçlarla ölçüm yapmalıyım?

Sabit arrival-rate senaryosu için k6, çalışma zamanı sayaçları için dotnet-counters, CPU/lock incelemesi için dotnet-trace kullanın. Limiter kapalı ve açık iki koşulda aynı süre, aynı istek gövdesi ve aynı veritabanı verisiyle p95/p99, 429, SQL command count, GC allocation rate ve thread-pool queue length değerlerini karşılaştırın.

c# eğitimi örneklerinde yerleşik RateLimiter çoklu pod ortamında global kota sağlar mı?

Hayır. Microsoft.AspNetCore.RateLimiting içindeki limiter state'i process belleğindedir; dört pod, aynı policy için dört ayrı token kovası demektir. Global kota için Redis'te Lua ile atomik INCR/EXPIRE, GCRA veya kontrollü bir merkezi kota servisi gerekir. Redis timeout durumunda fail-open mı fail-closed mu olunacağını endpointin maliyetine göre açıkça belirleyin.

Blazor eğitimi uygulamasında HTTP rate limiting SignalR Hub çağrılarını sınırlar mı?

HTTP middleware bağlantı kurulumu ve negotiate isteğini kapsar, ancak uzun ömürlü bağlantıdaki her Hub metodunu otomatik olarak kota altına almaz. IHubFilter içinde InvokeMethodAsync öncesinde kullanıcı kimliğiyle AcquireAsync çağırın; özellikle arama, dosya işleme başlatma ve canlı önizleme metotlarına ayrı limit uygulayın.

AI / LLM Discovery

Bu makale Opendart Akademi Microsoft / C# eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt