Ders 02 / 15
Hız Sınırlama Pencereleri
Aynı beş dakikalık yükün üç ayrı hız sınırı penceresinde koşturulması ve pencere biçiminin üç sayıyı nasıl kaydırdığı: sabit pencerenin sınır etkisi, kayan pencerenin meşru tepe yükünden kestiği istek, jeton kovasının tepe payını kurtarırken saldırı tepe yükünü de geçirmesi, meşru tepeyi kurtaran limitin kaç kötü isteği serbest bıraktığı ve kesilen hacmin hangi bölümünün daha erken bir aşamada kapatılabileceği.
İçindekiler
Önceki ders bir kural kümesini tek tek isteklere baktırdı ve kaçan bin iki yüz isteğin tam olarak bu yüzden kaçtığını gösterdi: hiçbiri tek başına yanlış değildi. Yanlışlık isteğin içinde değil, aynı istemciden gelen istek sayısında ve zamanlamasındaydı. Hız sınırlama bu eksene bakan savunmadır; ölçüsü ise durdurduğu kötü istek değil, meşru tepe yükünden kestiğidir.
Kısma algoritmaları daha önce ölçüldü ve burada tekrarlanmıyor. Bu dersin sorusu tektir: aynı nominal sınır üç ayrı pencere biçiminde uygulandığında üç sayı nereye kayıyor. Pencere biçimi bir uygulama ayrıntısı gibi görünür; ölçüldüğünde meşru işin kesilip kesilmediğini belirleyen ana karar çıkar.
Aynı Yük, Üç Pencere Biçimi
Bölgesel ölçüm ağının üretim ortamında beş dakikalık bir dilim alınıyor. Aşağıdaki her şey node
ile kurulan bir modeldir; gerçek trafik üretilmiyor, hiçbir istek gövdesi yazılmıyor.
- US6. Yedi istemci sınıfı, varış serileri ve saniyedeki istek sayıları kurgusal ölçüm ağına aittir. Meşru sınıflardan ikisinin tepe yükü işin kendisidir: vardiya başlangıcında saha uygulamasının açtığı oturumlar ve fatura döneminde çalışan toplu sorgu.
- US7. Hız sınırı istemci başına tek bir anahtara bağlıdır ve bu derste anahtar sabittir. Anahtarın hesap mı adres mi olduğu ayrı bir eksendir; sonraki ders onu ölçüyor.
- US8. Varış hızı saniye içinde düzgün dağılıyor. Gerçekte saniye içi kümelenme kayan pencerenin kestiği miktarı bir miktar artırır, yönünü değiştirmez.
- US9. Üç biçim aynı nominal sınırı taşıyor: dakikada altı yüz istek. Jeton kovası saniyede on jeton ekliyor ve dokuz yüz jetonda doluyor; ortalama hız aynı, tepe payı farklı.
- US10. “Daha erken kapatma aşaması” sütunu her kötü istemci için elle işaretlendi.
// yuk.mjs — bes dakikalik varis serisi ve uc pencere bicimi; model, gercek trafik degil // [istemci, gercek sinif, [[t0, t1, saniyedeki istek], ...], daha erken kapatma asamasi] export const SURE = 300; export const ISTEMCI = [ ['abone portali, surekli', 'mesru', [[0, 300, 4]], '-'], ['saha uygulamasi, vardiya baslangici', 'mesru', [[0, 110, 2], [110, 150, 30], [150, 300, 2]], '-'], ['fatura donemi toplu sorgu', 'mesru', [[0, 45, 1], [45, 65, 40], [65, 300, 1]], '-'], ['ic izleme yoklamasi', 'mesru', [[0, 300, 1]], '-'], ['is akisi disi toplu indirme', 'kotu', [[0, 300, 9]], 'tasarim'], ['sayfa gezinme kaziyicisi', 'kotu', [[0, 300, 26]], 'yok'], ['oturum acma denemesi dizisi', 'kotu', [[40, 100, 18], [160, 220, 18]], 'kod'], ]; export const seri = (prof) => { const a = new Array(SURE).fill(0); for (const [t0, t1, r] of prof) for (let t = t0; t < t1; t++) a[t] = r; return a; }; // sabit pencere: sayac pencere sinirinda sifirlanir export function sabit(a, limit, pen) { let gecen = 0, sayac = 0; for (let t = 0; t < a.length; t++) { if (t % pen === 0) sayac = 0; const izin = Math.min(a[t], Math.max(0, limit - sayac)); sayac += izin; gecen += izin; } return gecen; } // kayan pencere: son `pen` saniyede kabul edilen istek sayilir export function kayan(a, limit, pen) { let gecen = 0; const kabul = new Array(a.length).fill(0); for (let t = 0; t < a.length; t++) { let son = 0; for (let u = Math.max(0, t - pen + 1); u < t; u++) son += kabul[u]; kabul[t] = Math.min(a[t], Math.max(0, limit - son)); gecen += kabul[t]; } return gecen; } // jeton kovasi: saniyede `hiz` jeton eklenir, kova `kap` jetonda dolar export function kova(a, hiz, kap) { let gecen = 0, jeton = kap; for (let t = 0; t < a.length; t++) { jeton = Math.min(kap, jeton + hiz); const izin = Math.min(a[t], jeton); jeton -= izin; gecen += izin; } return gecen; } export const BICIM = [ ['sabit pencere 60 s', (a) => sabit(a, 600, 60)], ['kayan pencere 60 s', (a) => kayan(a, 600, 60)], ['jeton kovasi 10/s', (a) => kova(a, 10, 900)], ]; export function olc(bicim) { let yakalanan = 0, kacan = 0, yanlis = 0, mesru = 0; for (const [, sinif, prof] of ISTEMCI) { const a = seri(prof); const toplam = a.reduce((x, y) => x + y, 0); const kesilen = toplam - bicim(a); if (sinif === 'kotu') { yakalanan += kesilen; kacan += toplam - kesilen; } else { yanlis += kesilen; mesru += toplam; } } return { yakalanan, kacan, yanlis, mesru }; }
Üç biçim arasındaki tek fark, sayacın geçmişi nasıl tuttuğudur. Sabit pencere sayacı dakika başında sıfırlar; önceki dakikanın hiçbir isteğini hatırlamaz. Kayan pencere son altmış saniyede kabul ettiği her isteği hatırlar. Jeton kovası hiç pencere tutmaz: kullanılmayan payı biriktirir ve tek seferde harcamaya izin verir.
Meşru Tepe Yükünden Kesilen İstek
// kosum.mjs — ayni bes dakikalik yuk, degisen tek sey pencere bicimi import { ISTEMCI, BICIM, seri, olc } from './yuk.mjs'; const s = (x, n) => String(x).padStart(n); const T = (f) => ISTEMCI.filter(f).reduce((t, k) => t + seri(k[2]).reduce((x, y) => x + y, 0), 0); console.log(`${ISTEMCI.length} istemci, 300 saniye; toplam ${T(() => true)} istek` + ` (${T((k) => k[1] === 'kotu')} kotu / ${T((k) => k[1] === 'mesru')} mesru)\n`); console.log(`${'istemci'.padEnd(36)}${s('sinif', 6)}${s('istek', 7)}` + BICIM.map(([ad]) => s(ad.split(' ')[0], 9)).join('') + s('erken asama', 13)); for (const [ad, sinif, prof, asama] of ISTEMCI) { const a = seri(prof), toplam = a.reduce((x, y) => x + y, 0); console.log(`${ad.padEnd(36)}${s(sinif, 6)}${s(toplam, 7)}` + BICIM.map(([, f]) => s(toplam - f(a), 9)).join('') + s(asama, 13)); } console.log(`\n(uc sutun: o bicimin kestigi istek sayisi)\n`); console.log(`${'pencere bicimi'.padEnd(20)}${s('yakalanan', 11)}${s('kacan', 8)}` + `${s('yanlis kesilen', 16)}${s('mesru kesme %', 15)}`); for (const [ad, f] of BICIM) { const r = olc(f); console.log(`${ad.padEnd(20)}${s(r.yakalanan, 11)}${s(r.kacan, 8)}${s(r.yanlis, 16)}` + `${s((100 * r.yanlis / r.mesru).toFixed(1), 15)}`); }
7 istemci, 300 saniye; toplam 16960 istek (12660 kotu / 4300 mesru) istemci sinif istek sabit kayan jeton erken asama abone portali, surekli mesru 1200 0 0 0 - saha uygulamasi, vardiya baslangici mesru 1720 360 640 0 - fatura donemi toplu sorgu mesru 1080 45 240 0 - ic izleme yoklamasi mesru 300 0 0 0 - is akisi disi toplu indirme kotu 2700 0 0 0 tasarim sayfa gezinme kaziyicisi kotu 7800 4800 4800 3910 yok oturum acma denemesi dizisi kotu 2160 240 960 0 kod (uc sutun: o bicimin kestigi istek sayisi) pencere bicimi yakalanan kacan yanlis kesilen mesru kesme % sabit pencere 60 s 5040 7620 405 9.4 kayan pencere 60 s 5760 6900 880 20.5 jeton kovasi 10/s 3910 8750 0 0.0
Üç satır aynı nominal sınırı taşıyor ve üçünde de üç sayı farklı. Kayan pencere en çok kötü isteği durduruyor: beş bin yedi yüz altmış. Aynı satırda meşru yükün yüzde 20,5’i kesilmiş; kesilenler de rastgele istekler değil, vardiya başlangıcında saha uygulamasının açtığı altı yüz kırk oturum ile fatura döneminin iki yüz kırk sorgusudur. Sabit pencere daha az kesiyor, çünkü sayacı dakika sınırında sıfırlıyor; vardiya tepesi iki pencereye bölündüğü için üç yüz altmış istekle kurtuluyor. Aynı sıfırlama kötü tarafta da çalışıyor: oturum açma denemesi dizisinden yalnız iki yüz kırk istek kesiliyor, çünkü altmış saniyelik iki tepe de sınırın iki yanına dağılıyor. Sınır etkisi kimin lehine çalışacağını seçmez.
Jeton kovası satırı en dikkat çekici olanıdır. Meşru istekten hiçbir şey kesilmiyor: dokuz yüz jetonluk birikim vardiya tepesini de fatura tepesini de bütünüyle karşılıyor. Aynı birikim oturum açma denemesi dizisini de bütünüyle karşılıyor — iki bin yüz altmış isteğin tamamı geçiyor. Kova bir tepenin meşru mu olduğunu sormaz; yalnızca birikmiş pay olup olmadığına bakar. Tepe payı tanımak isteyen bir savunma, tepe biçimindeki saldırıyı da tanımak zorunda kalır.
İki bin yedi yüz isteklik iş akışı dışı toplu indirme ise üç biçimin hiçbirinde kesilmiyor. Saniyede dokuz istek, dakikada altı yüzlük sınırın altındadır; istemci sınırın hemen altında sabit hızla çalıştığı sürece hiçbir pencere biçimi onu görmez. Önceki dersin kural kümesinden kaçan kalem, hız sınırından da kaçıyor.
Limiti Yükseltmenin Bedeli
Kayan pencerenin kestiği sekiz yüz seksen meşru istek üretimde bir aksama demektir: vardiya başlangıcında saha ekibinin bir bölümü uygulamaya giremez. Verilen ilk yanıt limiti yükseltmektir.
// limit.mjs — mesru tepe yukunu kurtarmak icin limiti yukseltmenin bedeli import { ISTEMCI, seri, kayan, kova } from './yuk.mjs'; const s = (x, n) => String(x).padStart(n); const KOTU = ISTEMCI.filter((k) => k[1] === 'kotu'); const MESRU = ISTEMCI.filter((k) => k[1] === 'mesru'); const dizi = ISTEMCI.find((k) => k[0].startsWith('oturum')); const kes = (liste, f) => liste.reduce((t, k) => { const a = seri(k[2]); return t + a.reduce((x, y) => x + y, 0) - f(a); }, 0); console.log(`kayan pencere, 60 s; degisen tek sey limit`); console.log(`${'limit'.padEnd(7)}${s('yanlis kesilen', 16)}${s('yakalanan', 11)}${s('kacan', 8)}` + `${s('gecen oturum denemesi', 23)}`); for (const L of [600, 800, 1000, 1240, 1500]) { const f = (a) => kayan(a, L, 60); console.log(`${String(L).padEnd(7)}${s(kes(MESRU, f), 16)}${s(kes(KOTU, f), 11)}` + `${s(12660 - kes(KOTU, f), 8)}${s(f(seri(dizi[2])), 23)}`); } console.log(`\njeton kovasi, 10/s; degisen tek sey kova kapasitesi`); console.log(`${'kapasite'.padEnd(10)}${s('yanlis kesilen', 16)}${s('yakalanan', 11)}${s('kacan', 8)}` + `${s('gecen oturum denemesi', 23)}`); for (const K of [200, 400, 600, 900, 1200]) { const f = (a) => kova(a, 10, K); console.log(`${String(K).padEnd(10)}${s(kes(MESRU, f), 16)}${s(kes(KOTU, f), 11)}` + `${s(12660 - kes(KOTU, f), 8)}${s(f(seri(dizi[2])), 23)}`); }
kayan pencere, 60 s; degisen tek sey limit limit yanlis kesilen yakalanan kacan gecen oturum denemesi 600 880 5760 6900 1200 800 480 4360 8300 1600 1000 240 2960 9700 2000 1240 0 1600 11060 2160 1500 0 300 12360 2160 jeton kovasi, 10/s; degisen tek sey kova kapasitesi kapasite yanlis kesilen yakalanan kacan gecen oturum denemesi 200 1020 5190 7470 1580 400 620 4590 8070 1980 600 220 4210 8450 2160 900 0 3910 8750 2160 1200 0 3610 9050 2160
İki tablo aynı şeyi iki ayrı ayarla söylüyor. Kayan pencerede yanlış kesilen istek ancak limit bin iki yüz kırka çıktığında sıfıra iner; vardiya tepesinin altmış saniyelik en yoğun dilimi tam olarak o kadardır. Aynı satırda yakalanan istek beş bin yedi yüz altmıştan bin altı yüze düşmüş, kaçan istek altı bin dokuz yüzden on bir bin altmışa çıkmıştır. Son sütun kararın asıl bedelini yazıyor: oturum açma denemesi dizisinin tamamı geçmeye başlıyor.
Jeton kovası tablosu aynı eğriyi kapasite ekseninde tekrarlıyor. Meşru tepenin sıfır kesmeyle geçtiği ilk kapasite dokuz yüzdür; oturum açma dizisinin tamamının geçtiği ilk kapasite ise altı yüz. Yani kova, meşru tepeyi kurtaracak kadar gevşetilmeden önce saldırı tepesini kurtarmış oluyor. Sıralama tersine çevrilemez, çünkü her iki tepe de aynı şeydir: kısa süreli yüksek hız.
Buradan çıkan bir ayar önerisi değil, bir sınırdır: tepe yükü işin kendisi olan bir sistemde tek anahtara bağlı tek eşikli hız sınırı, meşru tepeyi geçirmek için saldırı tepesini de geçirmek zorundadır. Ayrım hızda değil, isteğin neye yapıldığındadır; hız sayacı bunu görmez.
Kesilenin Nereye Ait Olduğu
// asama.mjs — kesilen kotu istegin kaci daha ucuz bir asamada kapatilabilirdi import { ISTEMCI, BICIM, seri, kayan } from './yuk.mjs'; const s = (x, n) => String(x).padStart(n); const KOTU = ISTEMCI.filter((k) => k[1] === 'kotu'); const MESRU = ISTEMCI.filter((k) => k[1] === 'mesru'); const ASAMA = ['tasarim', 'kod', 'yok']; const kesilen = (k, f) => { const a = seri(k[2]); return a.reduce((x, y) => x + y, 0) - f(a); }; console.log(`${'pencere bicimi'.padEnd(20)}${ASAMA.map((a) => s(a, 10)).join('')}${s('yalniz burada %', 17)}`); for (const [ad, f] of BICIM) { const pay = ASAMA.map((a) => KOTU.filter((k) => k[3] === a).reduce((x, k) => x + kesilen(k, f), 0)); const t = pay.reduce((x, y) => x + y, 0); console.log(`${ad.padEnd(20)}${pay.map((p) => s(p, 10)).join('')}${s((100 * pay[2] / t).toFixed(1), 17)}`); } console.log(`\nkayan pencere; is akisi disi toplu indirmeyi (9/s) yakalamak icin gereken limit`); console.log(`${'limit'.padEnd(7)}${s('toplu indirmeden kesilen', 26)}${s('mesru kesilen', 15)}${s('mesru kesme %', 15)}`); const indirme = KOTU.find((k) => k[3] === 'tasarim'); for (const L of [600, 540, 480, 360]) { const f = (a) => kayan(a, L, 60); const m = MESRU.reduce((x, k) => x + kesilen(k, f), 0); console.log(`${String(L).padEnd(7)}${s(kesilen(indirme, f), 26)}${s(m, 15)}${s((100 * m / 4300).toFixed(1), 15)}`); }
pencere bicimi tasarim kod yok yalniz burada % sabit pencere 60 s 0 240 4800 95.2 kayan pencere 60 s 0 960 4800 83.3 jeton kovasi 10/s 0 0 3910 100.0 kayan pencere; is akisi disi toplu indirmeyi (9/s) yakalamak icin gereken limit limit toplu indirmeden kesilen mesru kesilen mesru kesme % 600 0 880 20.5 540 0 1000 23.3 480 300 1120 26.0 360 900 1360 31.6
Üst tablo katmanın gerçek payını veriyor. Sayfa gezinme kazıyıcısı hiçbir erken aşamada kapatılamaz: istekler tek tek meşrudur, kimliği doğrulanmıştır, yetkisi vardır ve yalnız birim zamandaki sayıları anlamlıdır. Üç biçimde de kesilen dört bin sekiz yüz — kovada üç bin dokuz yüz on — istek bu sınıftandır ve bu katmanın varlık nedeni tam olarak budur. Oturum açma denemesi dizisi ise farklı bir yere aittir: kod aşamasında ikinci etken zorunlu kılındığında dizinin değeri kalmaz, çünkü doğru parola bulunsa bile oturum açılmaz. Hız sınırının o dizide kestiği iki yüz kırk ya da dokuz yüz altmış istek, kapatılabilir bir kalemi her gün yeniden yakalamaktır.
Alt tablo tasarım aşamasına ait kalemi gösteriyor. İş akışı dışı toplu indirmeyi hız sınırıyla görmek için limitin dört yüz seksene indirilmesi gerekiyor; o ayarda bu istemciden kesilen istek üç yüz, meşru yükten kesilen ise bin yüz yirmidir — meşru işin dörtte biri. Limit üç yüz altmışa inince kesilen kötü istek dokuz yüze çıkıyor, meşru kesme yüzde 31,6’ya. Bu kalem hız sınırıyla ancak sistemi kullanılamaz hâle getirerek kapatılabilir; doğru yeri tasarımdır. Toplu indirme bir kuyruk işine alındığında uçta sınırın altında sabit hızla çalışma imkânı kalmaz.
Özet
- Aynı nominal sınır (dakikada 600 istek) üç pencere biçiminde üç ayrı sonuç verdi: sabit pencere 5.040 / 7.620 / 405, kayan pencere 5.760 / 6.900 / 880, jeton kovası 3.910 / 8.750 / 0 (yakalanan / kaçan / yanlış kesilen).
- Kayan pencere meşru yükün yüzde 20,5’ini kesti ve kesilenler vardiya başlangıcı ile fatura dönemi tepesiydi. Sabit pencerenin daha az kesmesi bir üstünlük değil, sınır etkisinin yan ürünüdür: aynı sıfırlama oturum açma dizisini de 960 yerine 240 istekle kurtardı.
- Jeton kovası meşru tepeden hiçbir şey kesmedi ama oturum açma denemesi dizisinin 2.160 isteğinin tamamını geçirdi. Kova kapasitesi meşru tepeyi kurtaracak kadar açıldığında (900) saldırı tepesi zaten 600 kapasitede kurtulmuştu.
- Kayan pencerede yanlış kesilen isteği sıfırlayan limit 1.240’tır; o ayarda yakalanan 5.760’tan 1.600’e düşer, kaçan 11.060’a çıkar. Tepe yükü işin kendisi olan bir sistemde tek eşikli hız sınırı meşru tepeyi geçirmek için saldırı tepesini de geçirir.
- Kesilen hacmin yalnız burada kapatılabilen bölümü kazıyıcıdır (4.800 / 3.910). Oturum açma dizisi kod aşamasında ikinci etkenle, iş akışı dışı toplu indirme tasarımda kuyruk işiyle kapanır; ikincisini hız sınırıyla görmek limiti 480’e indirmeyi, yani meşru işin dörtte birini kesmeyi gerektirir.
Sonraki Adım
Bu dersin sayacı tek bir anahtara bağlıydı ve o anahtarın ne olduğu hiç sorulmadı. Oturum açma denemesi dizisinin kesilen ya da kaçan bölümü de tam olarak bu yüzden belirsiz kaldı: dizinin saniyedeki hızı biliniyordu, ama denemelerin kaç hesaba ve kaç adrese dağıldığı bilinmiyordu. Aynı toplam hız, tek hesaba yüklendiğinde başka, on bin hesaba tek deneme olarak dağıtıldığında bambaşka bir saldırı biçimidir ve ikincisi bu dersin hiçbir ayarında görünmez. Sonraki ders eşiği hız ekseninden çıkarıp hesap ve adres eksenine taşıyor, hangi eşiğin hangi saldırı biçimini durdurduğunu ayırıyor ve aynı üç sayıyı orada soruyor — özellikle kilitlenen meşru hesabı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.