İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat