Ders 07 / 14
Hız Sınırlayıcı
İşi isteği reddetmek olan bileşenin tasarlanması: sabit pencere, kaydırmalı dilim ve belirteç kovasının izin verdiği aşımın üç istek deseninde ölçülmesi, pencere sınırındaki iki katına çıkan geçişin sayılması, dağıtık sayacın merkezî, bölünmüş kota ve gecikmeli eşitleme düzenlerinde ürettiği aşım ile yanlış ret sayısı ve eşitleme aralığının eşiği belirlemesi.
İçindekiler
Önceki iki vakada sistemin işi bir isteği karşılamaktı; başarısızlık kötü bir sonuçtu. Bu vaka tersini tasarlar: işi isteği reddetmek olan bir bileşeni. Hız sınırlama bir eylem olarak Trafik Katmanı ve Eşzamansız İşleme kurslarında kurulmuştu; orada kararın yeri ele alındı, algoritması değil. Burada tasarlanan şey hız sınırlayıcı bileşenin kendisidir.
Reddetme kararı paylaşılan bir sayaca bakarak verilir ve sayaç dağıtıktır. İki soru buradan çıkar: sayacın tuttuğu pencere hangi biçimde tanımlanırsa ilan edilen sınırın üstüne çıkılabilir, ve eşzamanlı okuyup yazan düğümler sayaçta ne kadar sapma üretir.
Kısıtlar ve Kapsam
İşlevsel gereksinimler: istemci başına isteği saymak, sınırı aşan isteği reddetmek, kalan hakkı ve bekleme süresini yanıtta bildirmek, sınırı bir kural kümesinden okumak.
Tasarlanmayan işler: kimlik doğrulama, kotanın ücretlendirilmesi, kötüye kullanım tespiti ve kural yönetim arayüzü.
İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: bir istemcinin herhangi bir 60 saniyelik aralıkta geçirdiği istek sayısı ilan edilen sınırın 1,1 katını geçmez (kaynak: sınırın istemciye verilmiş bir söz olması), sınırın altında kalan bir istemci hiç reddedilmez (kaynak: aynı sözün karşı yönü), sayaç deposu düştüğünde istek reddedilmez (kaynak: sınırlayıcının kendisinin kesinti kaynağı olmaması).
Varsayımlar ve Ölçek
| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| HS1 | izlenen istemci | 200.000 | sınır uygulanan ayrı anahtar sayısı |
| HS2 | uçta tepe istek | 40.000/s | sınırlayıcıdan geçen toplam istek hızı |
| HS3 | istemci başına ilan edilen sınır | 600 istek / 60 s | sözleşmede yazılı hak |
| HS4 | sınırlayıcı düğüm sayısı | 8 | uçtaki yatay ölçek |
| HS5 | sayaç eşitleme aralığı | 0,2 saniye | düğümlerin ortak görüntüyü tazeleme sıklığı |
| HS6 | sayaç kaydı | 48 bayt | anahtar, sayı, pencere damgası |
| HS7 | kaydırmalı pencere dilimi | 12 (5 saniyelik) | 60 saniyenin bölünme inceliği |
| HS8 | belirteç kovası kapasitesi | 60 belirteç | ani yığına bırakılan pay |
// hiz/olcek.mjs — HS varsayim tablosundan cikan kabaca buyukluk hesabi const HS = { istemci: 200_000, tepeIstek: 40_000, sinir: 600, pencereS: 60, dugum: 8, esitlemeS: 0.2, kayitBayt: 48, dilim: 12 }; // HS1..HS8 const dugumBasina = HS.tepeIstek / HS.dugum; console.log(`dugum basina tepe istek/s ${dugumBasina}`); console.log(`merkezi sayac kullanilirsa dokunus/s ${HS.tepeIstek}`); console.log(`istemci sinirinin saniye karsiligi ${HS.sinir / HS.pencereS}`); console.log(`\n${"sayac duzeni".padEnd(18)}${"istemci basina kayit".padStart(21)}${"toplam MB".padStart(11)}`); for (const [ad, n] of [["sabit pencere", 1], ["belirtec kovasi", 1], ["kaydirmali dilim", HS.dilim]]) console.log(`${ad.padEnd(18)}${String(n).padStart(21)}` + `${((HS.istemci * n * HS.kayitBayt) / 1e6).toFixed(2).padStart(11)}`); const korTavan = (HS.sinir / HS.pencereS) * HS.esitlemeS * HS.dugum; console.log(`\nesitleme koru: bir dugumun ${HS.esitlemeS} s icinde gormedigi istek = ` + `${dugumBasina * HS.esitlemeS} (tum istemciler)`); console.log(`tek istemci icin kor tavan = (sinir/s) x esitleme x dugum = ${korTavan} istek`); console.log(`esik: sinirin 1.1 kati = ${HS.sinir * 1.1}; kor tavanla ${HS.sinir + korTavan} -> ` + `${HS.sinir + korTavan <= HS.sinir * 1.1 ? "gecer" : "gecmez"}`);
dugum basina tepe istek/s 5000 merkezi sayac kullanilirsa dokunus/s 40000 istemci sinirinin saniye karsiligi 10 sayac duzeni istemci basina kayit toplam MB sabit pencere 1 9.60 belirtec kovasi 1 9.60 kaydirmali dilim 12 115.20 esitleme koru: bir dugumun 0.2 s icinde gormedigi istek = 1000 (tum istemciler) tek istemci icin kor tavan = (sinir/s) x esitleme x dugum = 16 istek esik: sinirin 1.1 kati = 660; kor tavanla 616 -> gecer
Bu sayılar hesap sınıfındadır. Üçü tasarımı belirliyor. Birincisi, merkezî bir sayaç kullanılırsa tepe uçta saniyede 40.000 dokunuş gerekir; bu, sınırlayıcının kendisini sistemdeki en yoğun yazma kaynağı yapar. İkincisi, kaydırmalı dilim 115,20 MB istiyor, sabit pencerenin 9,60 MB’ının tam 12 katı; incelik doğrudan belleğe yazılıyor. Üçüncüsü, kör tavan 16 istektir: 0,2 saniyelik eşitleme aralığında sekiz düğüm bir istemci için en çok 16 isteği birbirinden habersiz geçirebilir, bu da 616 eder ve 660 eşiğinin altında kalır. Eşitleme aralığı bu hesapla seçilmiştir.
Pencerenin Biçimi Aşımı Belirliyor
Ölçüm süreç içi bir modeldir: gerçek saat, ağ ya da depo yoktur; zaman model saatidir ve istek listesi belirlenimlidir. Üç desen denenir: sınırın iki katı hızda düzgün akış, pencere sınırının iki yanına yığılan akış ve sınırın tam üstünde ama aralıklı gelen akış. Ölçülen şey, kabul edilen isteklerin herhangi bir 60 saniyelik kayan aralıkta ulaştığı en yüksek sayıdır.
// hiz/pencere.mjs — uc pencere algoritmasinin surec ici modeli. Gercek saat, ag ya da depo // yoktur: zaman model saatidir (ms) ve istek listesi belirlenimlidir. const SINIR = 600, PENCERE = 60_000, DILIM = 5_000, SURE = 180_000; // HS3, HS7 const KOVA = 60, DOLDURMA = SINIR / (PENCERE / 1000); // HS8 const desen = { "duzgun 2x": () => Array.from({ length: (SURE / 1000) * 20 }, (_, i) => i * 50), "sinirda yigin": () => { const t = []; for (const sinir of [PENCERE, 2 * PENCERE]) for (const [bas, n] of [[sinir - DILIM, SINIR], [sinir, SINIR]]) for (let i = 0; i < n; i += 1) t.push(bas + Math.floor((i * DILIM) / n)); return t.sort((a, b) => a - b); }, "aralikli tam sinir": () => { const t = []; for (let k = 0; k < SURE / PENCERE; k += 1) for (let i = 0; i < SINIR; i += 1) t.push(k * PENCERE + Math.floor((i * DILIM) / SINIR)); return t; }, }; const ALGORITMA = { "sabit pencere": () => { let p = -1, n = 0; return (t) => { const k = Math.floor(t / PENCERE); if (k !== p) { p = k; n = 0; } return n < SINIR && (n += 1, true); }; }, "kaydirmali dilim": () => { const say = new Map(); return (t) => { const d = Math.floor(t / DILIM), ilk = d - PENCERE / DILIM + 1; for (const k of say.keys()) if (k < ilk) say.delete(k); let toplam = 0; for (const v of say.values()) toplam += v; if (toplam >= SINIR) return false; say.set(d, (say.get(d) ?? 0) + 1); return true; }; }, "belirtec kovasi": () => { let belirtec = KOVA, son = 0; return (t) => { belirtec = Math.min(KOVA, belirtec + ((t - son) / 1000) * DOLDURMA); son = t; return belirtec >= 1 && (belirtec -= 1, true); }; }, }; const enYogunPencere = (kabul) => { let en = 0; for (let i = 0, j = 0; i < kabul.length; i += 1) { while (kabul[i] - kabul[j] >= PENCERE) j += 1; en = Math.max(en, i - j + 1); } return en; }; console.log(`model: ${SURE / 1000} s, sinir ${SINIR}/${PENCERE / 1000} s, kova ${KOVA} belirtec, doldurma ${DOLDURMA}/s`); for (const [dad, uret] of Object.entries(desen)) { const istek = uret(); console.log(`\ndesen "${dad}": ${istek.length} istek`); console.log(`${"algoritma".padEnd(18)}${"kabul".padStart(8)}${"red".padStart(8)}` + `${"en yogun 60 s".padStart(15)}${"asim".padStart(8)}${"asim orani".padStart(12)}`); for (const [aad, kur] of Object.entries(ALGORITMA)) { const izin = kur(), kabul = istek.filter((t) => izin(t)); const en = enYogunPencere(kabul); console.log(`${aad.padEnd(18)}${String(kabul.length).padStart(8)}${String(istek.length - kabul.length).padStart(8)}` + `${String(en).padStart(15)}${String(en - SINIR).padStart(8)}${`x${(en / SINIR).toFixed(3)}`.padStart(12)}`); } }
model: 180 s, sinir 600/60 s, kova 60 belirtec, doldurma 10/s desen "duzgun 2x": 3600 istek algoritma kabul red en yogun 60 s asim asim orani sabit pencere 1800 1800 600 0 x1.000 kaydirmali dilim 1800 1800 600 0 x1.000 belirtec kovasi 1859 1741 659 59 x1.098 desen "sinirda yigin": 2400 istek algoritma kabul red en yogun 60 s asim asim orani sabit pencere 1800 600 1200 600 x2.000 kaydirmali dilim 1200 1200 600 0 x1.000 belirtec kovasi 318 2082 159 -441 x0.265 desen "aralikli tam sinir": 1800 istek algoritma kabul red en yogun 60 s asim asim orani sabit pencere 1800 0 600 0 x1.000 kaydirmali dilim 1800 0 600 0 x1.000 belirtec kovasi 327 1473 109 -491 x0.182
Bu sayılar ölçüm sınıfındadır ve belirlenimlidir; rastgelelik yoktur.
Birinci desen üç algoritmayı da sınırın içinde tutuyor, tek istisna belirteç kovasıdır: 659, yani 1,098 kat. Bu 59 isteklik fazlalık kovanın kapasitesidir ve HS8 doğrudan bu sayıyı seçer. 1,1 katlık eşikle karşılaştırıldığında kapasite 60’ın tavan olduğu görülüyor; 66’ya çıkarılsaydı eşik aşılırdı.
İkinci desen sabit pencerenin bilinen kusurunu sayıya çeviriyor: pencere sınırının iki yanına yığılan istekler iki ayrı sayacın içine düşüyor ve kayan 60 saniyelik aralıkta 1200 istek geçiyor, tam iki kat. Sınır 600 diye ilan edilmiş bir sistemde bir istemci 1200 istek geçirebiliyor. Kaydırmalı dilim aynı desende 600’de kalıyor.
Üçüncü desen belirteç kovasının bedelini gösteriyor. İstemci sınırın tam üstünde, penceresi başına 600 istek gönderiyor ve hiçbir sınırı aşmıyor; kaydırmalı dilim hepsini geçiriyor, belirteç kovası 1800 isteğin 1473’ünü reddediyor. Kova boşta geçen zamanı biriktirmez; aralıklı çalışan bir istemci hakkını kullanamaz. İkinci işlevsel olmayan gereksinim bu satırla ihlal ediliyor, dolayısıyla seçilen algoritma kaydırmalı dilimdir.
Dağıtık Sayacın Sapması
Seçilen algoritma tek düğümde doğru çalışıyor. Sekiz düğüm aynı istemciyi sayınca soru değişir. Üç düzen karşılaştırılır: her isteğin ortak sayaca dokunduğu merkezî düzen, sınırın düğüm sayısına bölündüğü bölünmüş kota ve düğümlerin yerel sayıp aralıkla eşitlendiği gecikmeli eşitleme.
// hiz/dagitik.mjs — dagitik sayacin surec ici modeli. Gercek dugum, ag ya da depo yoktur: // dugum atamasi tohumlu bir uretecle, esitleme model saatiyle yapilir. const SINIR = 600, PENCERE = 60, DUGUM = 8; // HS3, HS4 function uretec(tohum) { // 32 bitlik dogrusal esitlikli uretec let s = tohum >>> 0; return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; }; } export function kos({ duzen, hiz, aralik = 0.2, tohum }) { const rnd = uretec(tohum); const n = hiz * PENCERE; const yerel = new Array(DUGUM).fill(0); let global = 0, sonGlobal = 0, sonEsitleme = 0, kabul = 0, red = 0, dokunus = 0; for (let i = 0; i < n; i += 1) { const t = (i / hiz), d = Math.floor(rnd() * DUGUM); if (duzen === "gecikmeli esitleme" && t - sonEsitleme >= aralik) { sonGlobal = global; sonEsitleme = t; for (let j = 0; j < DUGUM; j += 1) yerel[j] = 0; } let izin; if (duzen === "merkezi") { dokunus += 1; izin = global < SINIR; } else if (duzen === "bolunmus kota") izin = yerel[d] < SINIR / DUGUM; else izin = sonGlobal + yerel[d] < SINIR; if (izin) { kabul += 1; global += 1; yerel[d] += 1; } else red += 1; } if (duzen === "gecikmeli esitleme") dokunus = DUGUM * Math.floor(PENCERE / aralik); return { istek: n, kabul, red, dokunus, asim: Math.max(0, kabul - SINIR) }; } const DUZEN = ["merkezi", "bolunmus kota", "gecikmeli esitleme"]; for (const hiz of [10, 90]) { console.log(`\nistemci hizi ${hiz}/s -> ${hiz * PENCERE} istek, sinir ${SINIR}, ${DUGUM} dugum, tohum 20260730`); console.log(`${"duzen".padEnd(20)}${"kabul".padStart(8)}${"red".padStart(7)}${"asim".padStart(7)}` + `${"yanlis ret".padStart(12)}${"sayac dokunusu".padStart(16)}`); for (const d of DUZEN) { const r = kos({ duzen: d, hiz, tohum: 20260730 }); const yanlis = hiz * PENCERE <= SINIR ? r.red : 0; console.log(`${d.padEnd(20)}${String(r.kabul).padStart(8)}${String(r.red).padStart(7)}` + `${String(r.asim).padStart(7)}${String(yanlis).padStart(12)}${String(r.dokunus).padStart(16)}`); } } console.log(`\ngecikmeli esitleme, istemci hizi 90/s (yigin)`); console.log(`${"esitleme araligi".padEnd(18)}${"kabul".padStart(8)}${"asim".padStart(7)}` + `${"asim orani".padStart(12)}${"dokunus/s".padStart(12)}${"esik 660".padStart(10)}`); for (const a of [0.2, 1, 2, 5]) { const r = kos({ duzen: "gecikmeli esitleme", hiz: 90, aralik: a, tohum: 20260730 }); console.log(`${`${a} s`.padEnd(18)}${String(r.kabul).padStart(8)}${String(r.asim).padStart(7)}` + `${`x${(r.kabul / SINIR).toFixed(3)}`.padStart(12)}${(r.dokunus / PENCERE).toFixed(1).padStart(12)}` + `${(r.kabul <= SINIR * 1.1 ? "gecer" : "gecmez").padStart(10)}`); }
istemci hizi 10/s -> 600 istek, sinir 600, 8 dugum, tohum 20260730 duzen kabul red asim yanlis ret sayac dokunusu merkezi 600 0 0 0 600 bolunmus kota 575 25 0 25 0 gecikmeli esitleme 600 0 0 0 2400 istemci hizi 90/s -> 5400 istek, sinir 600, 8 dugum, tohum 20260730 duzen kabul red asim yanlis ret sayac dokunusu merkezi 600 4800 0 0 5400 bolunmus kota 600 4800 0 0 0 gecikmeli esitleme 609 4791 9 0 2400 gecikmeli esitleme, istemci hizi 90/s (yigin) esitleme araligi kabul asim asim orani dokunus/s esik 660 0.2 s 609 9 x1.015 40.0 gecer 1 s 630 30 x1.050 8.0 gecer 2 s 720 120 x1.200 4.0 gecmez 5 s 900 300 x1.500 1.6 gecmez
İlk tablo bölünmüş kotayı eliyor. İstemci sınırının tam üstünde, 600 istek gönderiyor ve hiçbirini aşmıyor; buna karşın 25 isteği reddediliyor, yüzde 4,17. Sebep dağılım dengesizliğidir: istekler sekiz düğüme eşit dağılmaz ve payını dolduran düğüm, öteki düğümlerde kullanılmamış kota dururken reddeder. Kotayı bölmek sınırı bölmez, hakkı böler.
İkinci tablo merkezî sayacı fiyatlandırıyor. Aşımı sıfırdır, bu doğrudur; bedeli tepe uçta saniyede 40.000 sayaç dokunuşudur ve tek istemcilik bu modelde bile 5400 dokunuşla gecikmeli eşitlemenin 2400’ünün iki katından fazladır. Gecikmeli eşitleme 609 kabul ediyor, aşım 9.
Üçüncü tablo parametreyi seçiyor. Eşitleme aralığı büyüdükçe aşım büyüyor: 0,2 saniyede 1,015 kat, 1 saniyede 1,050 kat, 2 saniyede 1,200 kat. Eşik 1,1 kat olduğuna göre 1 saniye geçiyor, 2 saniye geçmiyor. Sayaç dokunuşu ise ters yönde küçülüyor: saniyede 40’tan 4’e. HS5’in 0,2 saniye seçilmesi bu iki eğrinin kesişme noktasıdır ve eşik 1,05’e sıkılaştırılsaydı aralığın 0,2 saniyede kalması zorunlu olurdu.
Tasarım, Arıza Davranışı ve Feda Edilen
Karar noktası ağ geçididir: Trafik Katmanı kursunun Ağ Geçidi Yük Boşaltma dersindeki
taşınabilirlik kuralı burada geçerlidir, çünkü istemci kimliği bir üstveri alanıdır ve karar
alan girdisi okumaz. Sayaç, Veri Katmanı Ölçekleme kursunun Depo Türleri dersindeki
anahtar–değer deposunda tutulur; anahtar (istemci, dilim) ikilisi, kayıt 48 bayt, süre sonu
60 saniyedir. Bileşenin kendisi Dayanıklılık ve Güvenilirlik kursunun Kısıtlama ve Yük Boşaltma
dersindeki kısıtlamadır: bilinen bir hızın üstünü keser, anlık kapasiteye bakmaz.
Bilerek kullanılmayan kalıp. Kuyruk tabanlı yük dengeleme (Uygulama Katmanı ve Servis Etkileşimi) kullanılmaz: aşan isteği kuyruğa alıp sonra işlemek sınırı bir gecikmeye çevirir, oysa bileşenin sözleşmesi reddetmektir.
Arıza senaryosu sayaç deposunun düşmesidir. Üçüncü eşik gereği sınırlayıcı açık kalır: düğümler son bilinen ortak görüntüyle ve yalnız kendi yerel sayacıyla karar verir. Bu, bölünmüş kotanın davranışına yaklaşmaktır ve arıza süresince istemci başına aşım sekiz kata kadar çıkabilir. Feda edilen tek cümlededir: bu tasarım aşımı sıfırlamaz, onu 1,015 katta tutmayı 40.000 yerine 40 sayaç dokunuşuyla satın alır.
Özet
- Sabit pencere, sınırın iki yanına yığılan istekte kayan 60 saniyelik aralıkta 1200 istek geçirdi: ilan edilen sınırın tam iki katı.
- Belirteç kovası düzgün akışta 659 geçirdi (1,098 kat, kova kapasitesi kadar), ama sınırın tam üstünde aralıklı çalışan istemcinin 1800 isteğinden 1473’ünü reddetti.
- Kaydırmalı dilim üç desende de 600’de kaldı ve hiçbir geçerli isteği reddetmedi; bedeli 12 kat bellektir, 9,60 MB yerine 115,20 MB.
- Bölünmüş kota, sınırının tam üstündeki bir istemcinin 25 isteğini (yüzde 4,17) dağılım dengesizliği yüzünden reddetti; kotayı bölmek hakkı böler.
- Merkezî sayacın aşımı sıfırdır ama tepe uçta saniyede 40.000 dokunuş ister; gecikmeli eşitleme 0,2 saniyelik aralıkta 1,015 kat aşımı saniyede 40 dokunuşla veriyor.
- Eşitleme aralığı eşiği belirliyor: 1 saniyede 1,050 kat (geçer), 2 saniyede 1,200 kat (geçmez).
Sonraki Adım
Bu vakada sayaç bir eşiği koruyordu ve eşik aşıldığında kaybedilen şey bir istekti; istemci yeniden dener. Sonraki vaka aynı sayma sorununu, sayılan şeyin geri getirilemez olduğu yerde sorar. Sınırlı sayıda koltuk ya da stok kalemi vardır; sayacın iki fazla saymasının karşılığı reddedilmiş bir istek değil, satılmış ama var olmayan bir kalemdir. O zaman ölçülecek sayı çekişme altında üretilen fazla satıştır ve yanında ikinci bir sayı durur: aynı korumanın reddettiği geçerli istek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.