İçeriğe geç
academia.sh

Ders 04 / 14

İçerik Dağıtım Tasarımı

Statik ve dinamik trafiğin ayrılması: ayrımın kökene inen istek sayısına ve bayta etkisinin kenar önbelleği modeliyle ölçülmesi, eşiğin gerektirdiği isabet oranının hesaptan türetilmesi, istek isabeti ile bayt isabetinin ayrılması ve sürümlü adın bayatlık penceresini sıfırlaması.

İçindekiler

Üç vakada da yanıt bir kayıttı, taşınan bayt küçüktü ve pahalı kalem hesap ya da bellekti. Bu vakada aynı sistem iki farklı trafik taşıyor: birkaç yüz baytlık kişiye özel yanıtlar ve her istekte tekrar gönderilen büyük, değişmeyen statik varlıklar. İkisi aynı yoldan geçtiğinde birinin özelliği ötekini bozar — kişiye özel yanıt önbelleklenemez, ve bütün yol o yüzden önbelleklenemez sayılır. Bu ders ayrımın kökene inen istek sayısına ve bayta ne yaptığını ölçer.

Kısıtlar

İşlevsel gereksinimler. I1: uygulama kabuğu ve varlıkları (betik, biçem, görsel) istemciye ulaşır. I2: kişiye özel veri isteği yanıtlanır. I3: yeni sürüm yayımlandığında istemci eskisini kullanmaz. I4: yayımlanan bir sürüm geri alınabilir.

Kapsam daraltması. Görsellerin yeniden boyutlandırılması ve kodlanması, çevrimdışı çalışma ve kabuğun sunucuda mı istemcide mi kurulacağı dışarıdadır; üçü de taşınan bayt sayısını değiştirir, trafiğin nereden ayrılacağını değil.

Kod Eşik Eşiğin kaynağı
G1 kişiye özel yanıtın ortancası 150 ms’yi geçmez ekranda veri beklenen süre
G2 kökene saniyede 2000’den fazla istek inmez kökenin kurulu kapasitesi
G3 yeni sürüm 300 saniye içinde bütün istemcilerde etkin olur hatalı sürümün geri alınabilmesi
G4 kenar düğümü başına önbellek 8 GB’yi geçmez düğümün yerel depolama payı

Varsayımlar

Kod Varsayım Değer Gerekçe
VI1 günlük oturum 4.000.000 uygulamayı açan kullanıcı sayısı
VI2 oturum başına statik varlık isteği 25 betik, biçem ve görsellerin toplamı
VI3 oturum başına kişiye özel istek 8 ekranı dolduran veri çağrıları
VI4 tepe çarpanı 3 tepe saatteki hızın gün ortalamasına oranı
VI5 statik varlık boyutu taban 20 KB, üs 2, tavan 4 MB birkaç büyük paket, çok sayıda küçük varlık; ortalaması 40 KB
VI6 kişiye özel yanıt 6 KB ekranı dolduran alanlar
VI7 farklı statik varlık 500.000 sürümlü adlar ve görseller dahil
VI8 statik istek dağılımı Zipf, üs 0,9 az sayıda varlık her oturumda, çoğu seyrek istenir

Kabaca Büyüklük Hesabı

// dagitim/hesap.mjs — VI1–VI8'den cikan hesap ve G2'nin gerektirdigi isabet oraninin turetilmesi
const VI = { oturum: 4e6, statikIstek: 25, dinamikIstek: 8, tepe: 3, varlikBayt: 40_960, yanitBayt: 6144 };
const GUN = 86_400, KOKEN = 2000;                     // KOKEN: G2 esigi, istek/s
const b = (x, n = 2) => x.toFixed(n);
const mbit = (x) => (x * 8) / 1e6;

const statikTepe = (VI.oturum * VI.statikIstek / GUN) * VI.tepe;
const dinamikTepe = (VI.oturum * VI.dinamikIstek / GUN) * VI.tepe;
const statikBayt = statikTepe * VI.varlikBayt, dinamikBayt = dinamikTepe * VI.yanitBayt;
console.log(`tepe statik istek/s = ${b(statikTepe)}   tepe dinamik istek/s = ${b(dinamikTepe)}   toplam = ${b(statikTepe + dinamikTepe)}`);
console.log(`istek orani statik/dinamik = ${b(statikTepe / dinamikTepe)}   bayt orani = ${b(statikBayt / dinamikBayt)}`);
console.log(`tepe cikis: statik ${b(mbit(statikBayt))} Mbit/s, dinamik ${b(mbit(dinamikBayt))} Mbit/s`);
console.log(`G2 esigi ${KOKEN} istek/s; ayrimsiz tasarim ${b(statikTepe + dinamikTepe - KOKEN)} istek/s asiyor`);
console.log(`G2'yi tutmak icin gereken statik istek isabeti = ${b(1 - (KOKEN - dinamikTepe) / statikTepe, 4)}`);
console.log(`dinamik taban tek basina kokene ${b(dinamikTepe)} istek/s = toplamin ${b(dinamikTepe / (statikTepe + dinamikTepe), 4)}'i`);

const iki = (VI.oturum * VI.statikIstek * 2 / GUN) * VI.tepe;
console.log(`VI2 duyarliligi: oturum basina statik istek 25 -> 50 ise tepe statik ${b(iki)} istek/s ve gereken isabet ${b(1 - (KOKEN - dinamikTepe) / iki, 4)}`);
tepe statik istek/s = 3472.22   tepe dinamik istek/s = 1111.11   toplam = 4583.33
istek orani statik/dinamik = 3.13   bayt orani = 20.83
tepe cikis: statik 1137.78 Mbit/s, dinamik 54.61 Mbit/s
G2 esigi 2000 istek/s; ayrimsiz tasarim 2583.33 istek/s asiyor
G2'yi tutmak icin gereken statik istek isabeti = 0.7440
dinamik taban tek basina kokene 1111.11 istek/s = toplamin 0.2424'i
VI2 duyarliligi: oturum basina statik istek 25 -> 50 ise tepe statik 6944.44 istek/s ve gereken isabet 0.8720

Dört sayı tasarımı belirliyor. Birincisi iki oranın birbirinden farklı olması: statik trafik istek olarak dinamiğin 3,13 katı, bayt olarak 20,83 katı. Tek bir “trafik” sayısıyla konuşmak bu yüzden yanıltıcıdır — hangi kaynağın sıkıştığına göre iki farklı yanıt çıkar. İkincisi ayrımsız tasarımın G2’yi 2583,33 istek/s ile aşması. Üçüncüsü eşiğin bir isabet oranına dönüşmesi: kökeni 2000 istek/s’nin altında tutmak için statik isabetin en az 0,7440 olması gerekiyor. Dördüncüsü tabanın kendisi: dinamik akış tek başına kökene 1111,11 istek/s indiriyor ve bu, isabet oranı ne olursa olsun inmez; toplam isteğin 0,2424’ü hiçbir önbellekle azalmaz.

Ayrımın Ölçülmesi

// dagitim/ayrim.mjs — statik ve dinamik ayriminin kokene inen istege ve bayta etkisi.
// Kenar onbellegi en son kullanilani tutan bir Map'tir (ekleme sirasi = kullanim sirasi).
// Varlik boyutlari ve istek akisi kendi yazilmis uretecle uretilir, tohum sabittir. MODELDIR.
const TOHUM = 20260730, VARLIK = 500_000, ISTEK = 1_800_000, ISINMA = 600_000;
const TABAN = 20_480, US = 2.0, TAVAN = 4 << 20, ZIPF = 0.9;   // VI5 ve VI8
const STATIK_TEPE = 3472.22, DINAMIK_TEPE = 1111.11, DINAMIK_BAYT = 6144;   // hesap blogundan
let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;

const boyut = new Int32Array(VARLIK);
for (let i = 0; i < VARLIK; i += 1) boyut[i] = Math.min(TAVAN, Math.round(TABAN * Math.pow(rast(), -1 / US)));
const toplamBayt = boyut.reduce((a, b) => a + b, 0);
// Zipf(0,9) tersten ornekleme: sira = (1 + u (N^(1-z) - 1))^(1/(1-z))
const ust = Math.pow(VARLIK, 1 - ZIPF);
const cek = () => Math.min(VARLIK - 1, Math.floor(Math.pow(1 + rast() * (ust - 1), 1 / (1 - ZIPF))) - 1);
const istekler = new Int32Array(ISTEK);
for (let i = 0; i < ISTEK; i += 1) istekler[i] = cek();

function kosum(kapasite) {
  const onbellek = new Map();
  let gezgin = onbellek.keys();
  let dolu = 0, isabet = 0, isabetBayt = 0, olcum = 0, olcumBayt = 0;
  for (let i = 0; i < ISTEK; i += 1) {
    const v = istekler[i], bt = boyut[v], sayiliyor = i >= ISINMA;
    if (sayiliyor) { olcum += 1; olcumBayt += bt; }
    if (onbellek.has(v)) {
      onbellek.delete(v); onbellek.set(v, bt);
      if (sayiliyor) { isabet += 1; isabetBayt += bt; }
      continue;
    }
    if (bt > kapasite) continue;                       // sigmayan varlik onbellege alinmaz
    while (dolu + bt > kapasite) {                     // en eski kullanilan cikarilir
      let n = gezgin.next();
      if (n.done) { gezgin = onbellek.keys(); n = gezgin.next(); }
      const eskiB = onbellek.get(n.value);
      if (eskiB !== undefined) { onbellek.delete(n.value); dolu -= eskiB; }
    }
    onbellek.set(v, bt); dolu += bt;
  }
  return { istekIsabet: isabet / olcum, baytIsabet: isabetBayt / olcumBayt, tutulan: onbellek.size };
}

const b = (x, n = 2) => x.toFixed(n);
const mbit = (baytS) => (baytS * 8) / 1e6;
console.log(`model: ${VARLIK.toLocaleString("tr-TR")} varlik, toplam ${b(toplamBayt / 1e9)} GB, ortalama ` +
  `${b(toplamBayt / VARLIK / 1024)} KB, ${ISTEK.toLocaleString("tr-TR")} istek (ilk ${ISINMA.toLocaleString("tr-TR")} isinma)`);
const ayrimsizIstek = STATIK_TEPE + DINAMIK_TEPE;
const ayrimsizBayt = STATIK_TEPE * (toplamBayt / VARLIK) + DINAMIK_TEPE * DINAMIK_BAYT;
console.log(`\nayrim yok (kisiye ozel belge butun yolu onbelleklenemez kiliyor):`);
console.log(`  kokene inen ${b(ayrimsizIstek)} istek/s ve ${b(mbit(ayrimsizBayt))} Mbit/s`);

console.log(`\n${"kenar onbellegi".padStart(16)}${"tutulan varlik".padStart(16)}${"istek isabeti".padStart(15)}` +
  `${"bayt isabeti".padStart(14)}${"kokene istek/s".padStart(16)}${"kokene Mbit/s".padStart(15)}${"istek kati".padStart(12)}${"bayt kati".padStart(11)}`);
for (const gb of [0.5, 2, 8, 20.5]) {
  const r = kosum(gb * 1e9);
  const kokIstek = STATIK_TEPE * (1 - r.istekIsabet) + DINAMIK_TEPE;
  const kokBayt = STATIK_TEPE * (1 - r.baytIsabet) * (toplamBayt / VARLIK) + DINAMIK_TEPE * DINAMIK_BAYT;
  console.log(`${`${gb} GB`.padStart(16)}${r.tutulan.toLocaleString("tr-TR").padStart(16)}${b(r.istekIsabet, 4).padStart(15)}` +
    `${b(r.baytIsabet, 4).padStart(14)}${b(kokIstek).padStart(16)}${b(mbit(kokBayt)).padStart(15)}${b(ayrimsizIstek / kokIstek).padStart(12)}${b(ayrimsizBayt / kokBayt).padStart(11)}`);
}
model: 500.000 varlik, toplam 20.44 GB, ortalama 39.93 KB, 1.800.000 istek (ilk 600.000 isinma)

ayrim yok (kisiye ozel belge butun yolu onbelleklenemez kiliyor):
  kokene inen 4583.33 istek/s ve 1190.40 Mbit/s

 kenar onbellegi  tutulan varlik  istek isabeti  bayt isabeti  kokene istek/s  kokene Mbit/s  istek kati  bayt kati
          0.5 GB          12.118         0.4562        0.4390         2999.18         691.80        1.53       1.72
            2 GB          49.111         0.6188        0.6059         2434.84         502.20        1.88       2.37
            8 GB         196.218         0.8272        0.8209         1711.28         258.06        2.68       4.61
         20.5 GB         324.829         0.8773        0.8727         1537.00         199.14        2.98       5.98

Model varlıkların ortalamasını 39,93 KB buluyor; hesap bloğu VI5’in kuramsal ortalamasını (40 KB) kullandığı için iki bloğun bayt sayıları ikinci hanede ayrışıyor. Ölçülen değer tavanla kesilmiş dağılımın gerçek ortalamasıdır.

Ayrım G2’yi ancak yeterli önbellekle tutuyor. G4’ün izin verdiği 8 GB’de istek isabeti 0,8272, gereken 0,7440’ın üstünde; kökene 1711,28 istek/s iniyor ve eşik tutuyor. 2 GB’de isabet 0,6188’e, köken 2434,84 istek/s’ye çıkıyor ve G2 kırılıyor. Önbellek boyutu bu tasarımda bir kolaylık değil, eşiği tutan parametredir.

Ayrım isteği ve baytı aynı oranda azaltmıyor. 8 GB’de istek 2,68 kat, bayt 4,61 kat azalıyor. Nedeni hesapta duruyordu: dinamik akış isteklerin 0,2424’ünü tutuyor ama baytların yirmide birinden azını. Önbellek statik tarafı kaldırdıkça geriye istek olarak ağır, bayt olarak hafif bir taban kalıyor. Bir tasarım “kökeni yüzde yetmiş azalttık” cümlesiyle savunulamaz; hangi sayının azaldığı söylenmelidir.

İstek isabeti ile bayt isabeti ayrı sayılardır. Her satırda bayt isabeti istek isabetinden düşük (0,8272’ye karşı 0,8209): büyük varlıklar önbellekten daha çabuk çıkıyor, çünkü yerlerine onlarca küçük varlık sığıyor. Fark küçüktür ama işareti sabittir ve önbellek küçüldükçe büyür (0,5 GB’de 0,4562’ye karşı 0,4390).

Tasarım

  • İçerik dağıtım ağı ve kenar önbelleği (Trafik Katmanı, İçerik Dağıtım Ağları). Parametre düğüm başına 8 GB‘dir ve ölçülen istek isabeti 0,8272 ile G2’nin gerektirdiği 0,7440’ın üstündedir.
  • Statik içerik barındırma (Trafik Katmanı, Statik İçerik Barındırma). Parametre sürümlü addır: içeriği değişen varlığın adı da değişir, bu yüzden ömrü sınırsız verilebilir.
  • API ağ geçidi (Trafik Katmanı, API Ağ Geçidi). Dinamik yol buradan geçer; parametre, kökene inen 1111,11 istek/s’lik tabandır.
  • Ön yüz için backend (Trafik Katmanı, Ön Yüz İçin Backend). Parametre oturum başına 8 kişiye özel istektir; bu sayı düşerse taban da düşer, çünkü taban önbellekle değil birleştirmeyle azaltılır.
  • İtme tabanlı dağıtım (Trafik Katmanı, İtme ve Çekme Tabanlı Dağıtım). Yeni sürümün varlıkları yayımdan önce kenara itilir; parametre G3’ün 300 saniyelik penceresidir.
  • Zarif bozulma (Dayanıklılık ve Güvenilirlik, Zarif Bozulma). Köken düştüğünde kenar elindeki varlıkları sunmaya devam eder; parametre, ayakta kalan istek payıdır.

Bilerek kullanılmayan iki kalıp. Önden tazeleme (Veri Katmanı Ölçekleme, Önden Tazeleme) kullanılmıyor: sürümlü adda bir varlık hiç bayatlamaz, tazelenecek bir şey yoktur. Oturum yapışkanlığı (Trafik Katmanı, Oturum Yapışkanlığı) kullanılmıyor: statik yolda durum yoktur ve yapışkanlık kenar önbelleğinin paylaşılmasını engelleyerek isabeti düşürürdü.

Elenen Alternatif: Tek Yol, Kısa Ömürlü Önbellek

Alternatif tasarım iki trafiği ayırmaz; hepsini aynı yoldan geçirir ve statik varlıklara kısa bir önbellek ömrü verir. Kazandığı şey gerçektir: tek dağıtım hattı, tek yayım adımı, sürüm karışması olmaz — belge ve varlıklar hep birlikte tazelenir. Yayımın atomik kalması küçük bir kazanç değildir.

Eleme sayısı kökendedir. Kişiye özel belge önbelleklenemediği için tek yolda önbellekleme yalnız varlıklara verilebilir ve ömrü kısa tutulmak zorundadır, çünkü adları değişmez; bu da isabet oranını tablonun 0,5 GB satırına yaklaştırır. Ayrımsız tasarımda köken 4583,33 istek/s ve 1190,40 Mbit/s alır — G2’yi 2583,33 istek/s aşar ve hiçbir önbellek boyutu bunu kurtarmaz, çünkü sorun boyut değil önbelleklenebilirliktir. Alternatif hangi kısıt değişirse kazanır: kökenin kapasitesi 5000 istek/s olsaydı G2 kendiliğinden tutar ve ayrımın tek gerekçesi bant genişliği kalırdı.

Arıza Davranışı ve Feda Edilen

Köken düşerse statik yol ayakta kalır: kenar isteklerin 0,8272’sini elindeki varlıklarla karşılamaya devam eder ve sürümlü ad sayesinde sunduğu şey bayat değil, doğru sürümdür. Düşen, dinamik tabanın tamamıdır — isteklerin 0,2424’ü. Uygulama açılır ama verisi gelmez; bu, zarif bozulmanın bu vakadaki biçimidir.

Bir kenar düğümü düşerse yükü öteki düğümler alır ama önbellekleri o varlıklar için soğuktur. Soğuk önbelleğin alt sınırını tablonun ilk satırı veriyor: 0,4562 isabetle köken 2999,18 istek/s alır ve G2 kırılır. Devralmanın maliyeti burada kapasite değil, ısınma süresidir.

Feda edilen yayımın atomikliğidir. İki yol iki hızda ilerlediği için G3’ün 300 saniyelik penceresinde bir istemci yeni belgeyle eski varlığı ya da tersini görebilir. Sürümlü ad bayatlık penceresini statik tarafta sıfırladı, ama karşılığında iki tarafın birbirine göre bayatlığı diye yeni bir pencere açtı.

Özet

  • Statik trafik istek olarak dinamiğin 3,13 katı, bayt olarak 20,83 katı; tek bir trafik sayısıyla yapılan savunma hangi kaynağın sıkıştığını gizler.
  • G2’nin 2000 istek/s eşiği bir isabet oranına dönüşüyor: statik isabet en az 0,7440 olmalı. G4’ün izin verdiği 8 GB’de ölçülen isabet 0,8272, 2 GB’de 0,6188 ve eşik kırılıyor.
  • Ayrım isteği 2,68, baytı 4,61 kat azaltıyor; fark, dinamik tabanın isteklerin 0,2424’ünü ama baytların çok küçük bir payını tutmasından geliyor.
  • Bayt isabeti istek isabetinden hep düşük (8 GB’de 0,8209’a karşı 0,8272) ve önbellek küçüldükçe fark büyüyor, çünkü büyük varlıklar önce çıkarılıyor.
  • Sürümlü ad statik tarafta bayatlık penceresini sıfırlıyor; önden tazeleme bu yüzden gereksiz.
  • Feda edilen yayımın atomikliğidir: 300 saniyelik pencerede yeni belge eski varlıkla eşleşebilir.

Sonraki Adım

Dört vaka aynı ailedendi. Bağlantı kısaltmada okuma yazmanın 100 katıydı, haber akışında 40, arama önerisinde 12, içerik dağıtımında dinamik istek bile önbellekli statiğin yanında küçük kaldı. Bu oran her dört tasarımın da belkemiğiydi: iş okuma tarafından yazma tarafına kaydırıldı (kutuya dağıtma, hazır öneri listesi, önek ağacı), çünkü yazma seyrekti ve kaydırılan işin bedeli ucuzdu. Hepsinde ikinci bir ortak nokta vardı: bayatlık ödenebilir bir bedeldi. Kenarda 300 saniye eski bir yönlendirme, 60 saniye görünmeyen bir gönderi, 600 saniye önerilmeyen bir sorgu, iki tarafın birbirine göre kayması — hiçbiri kabul edilemez sayılmadı, çünkü bir okumanın biraz eski olması düzeltilebilir bir hatadır.

Yazmanın hâkim olduğu bir sistemde bu kararların ikisi de tutmaz. Yazma seyrek değilse okuma tarafına iş kaydırmak bir kazanç değil bir çarpandır; ve bir kaydın yanlış olması kabul edilemiyorsa bayatlık artık ödenebilir bir bedel değil, doğrudan bir arızadır. Sonraki konu bu tersine dönmüş kısıtlarla açılır: yazmanın okumayı geçtiği, sıranın ve teslimin garanti altına alınması gerektiği ve bir kaydın iki kez işlenmesinin gerçek bir zarar ürettiği sistemler.

İ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