Ders 02 / 16
İçerik Dağıtım Ağları
Kenar önbelleğinin bir konum kararı olarak ele alınması: yerel köken ve kenar süreçleriyle tel üzerindeki baytın ölçülmesi, süreç içi izle kenar isabet oranının ve uygulamaya ulaşan istek sayısının sayılması, iki önbelleğin seri bağlanmasının ikinciyi işlevsiz bırakması ve giriş kursunun hangi hesabının değiştiğinin gösterilmesi.
İçindekiler
Ad katmanı isteğin hangi bölgeye gideceğini seçti, ama seçtiği şey bir adresti; istek o adrese
vardığında hâlâ uygulamaya ulaşmamıştır. Aradaki boşlukta isteği uygulamaya hiç ulaştırmadan
yanıtlayabilecek bir durak vardır. Önbellekleme Eşzamansız İşleme kursunda kuruldu ve orada
önbellek uygulamanın yanındaydı; Sistem Tasarımına Giriş kursunun hesabı da öyle varsayıyordu ve
yüzde 90’lık isabet oranı onbellek arkasi okuma/s sayısını 41,67’ye indiriyordu.
Bu ders aynı önbelleği bir yer değiştirmeyle ele alır. Strateji, geçersizleştirme ve anahtar tasarımı yeniden anlatılmaz; burada değişen tek şey önbelleğin nerede durduğudur. İçerik dağıtım ağı (content delivery network), aynı içeriği kullanıcıya yakın çok sayıda noktada tutan bir düzendir ve o noktaların her birine kenar önbelleği (edge cache) denir. Sorulacak soru şudur: aynı isabet oranı uygulamanın yanında değil kullanıcının yanında elde edilirse hangi sayı değişir, hangisi değişmez.
Konum Kararı Ne Değiştirir
Bir önbelleğin uygulamanın yanında durması ile kullanıcının yanında durması, isabet oranı aynı kalsa bile üç şeyi ayırır. Birincisi, isabet edilen istek uygulamaya hiç varmaz; kenarda karşılanan bir istek uygulama süreçlerinin gördüğü yükten düşer. İkincisi, isabet edilen isteğin yanıtı köken ile kenar arasındaki bağı kullanmaz; sınırdan geçen bayt azalır. Üçüncüsü, isabet edilmeyen istek artık iki durak yerine üç duraktan geçer.
Bunların hiçbiri isabet oranını kendiliğinden büyütmez. Bu dersin ölçtüğü şey, konumun oranı değiştirmediği ama oranın karşılığını değiştirdiğidir.
Tel Üzerindeki Bayt
İlk düzenek iki yerel süreçtir: köken ve kenar. Ölçülen, aynı gövdenin gerçek bir bağlantı üzerinde kaç bayt tuttuğudur. Giriş kursunun hesabı bunu dışarıda bırakmış ve “başlık ve protokol yükü ayrı bir varsayım olurdu” demişti; burada varsayım yerine ölçüm konur.
// kenar/sunucu.mjs — iki yerel surec: koken 8471'de, kenar onbellegi 8472'de dinler. Onbellek // kapasiteleri ornegi kucuk tutmak icin uc anahtardir; her ikisi de sayacini /sayac'ta acar. import http from "node:http"; const GOVDE = 480; // K01 V5: takip yaniti govdesi const govde = (no) => { const bas = JSON.stringify({ takip: no, durum: "aktarma", bolge: "b34" }); return bas + " ".repeat(GOVDE - bas.length); }; const yeniOnbellek = (kapasite) => ({ kapasite, m: new Map(), al(no) { const g = this.m.get(no); if (g !== undefined) this.m.delete(no); return g; }, koy(no, g) { if (this.m.size >= this.kapasite) this.m.delete(this.m.keys().next().value); this.m.set(no, g); }, }); const sayacYolu = (s) => (istek, yanit) => { yanit.writeHead(200, { "content-type": "application/json" }); yanit.end(JSON.stringify(s)); }; const ks = { istek: 0, isabet: 0, depo: 0 }; const ko = yeniOnbellek(3); http.createServer((istek, yanit) => { if (istek.url === "/sayac") return sayacYolu(ks)(istek, yanit); const no = istek.url.slice("/takip/".length); ks.istek += 1; let g = ko.al(no); if (g === undefined) { ks.depo += 1; g = govde(no); } else ks.isabet += 1; ko.koy(no, g); yanit.writeHead(200, { "content-type": "application/json", "content-length": GOVDE }); yanit.end(g); }).listen(8471); const es = { istek: 0, isabet: 0, iska: 0, kokenIstek: 0, kokenBayt: 0 }; const eo = yeniOnbellek(3); http.createServer(async (istek, yanit) => { if (istek.url === "/sayac") return sayacYolu(es)(istek, yanit); const no = istek.url.slice("/takip/".length); es.istek += 1; let g = eo.al(no), durum = "isabet"; if (g === undefined) { durum = "iska"; es.iska += 1; es.kokenIstek += 1; g = await (await fetch(`http://127.0.0.1:8471/takip/${no}`)).text(); es.kokenBayt += Buffer.byteLength(g); } else es.isabet += 1; eo.koy(no, g); yanit.writeHead(200, { "content-type": "application/json", "content-length": Buffer.byteLength(g), "x-kenar": durum }); yanit.end(g); }).listen(8472);
# kenar-olc.sh — once kokene dogrudan bir istek, sonra kenar uzerinden ayni iki takip numarasi # ucer kez sorulur. Baslik baytlari sunucunun urettigi baslik kumesine baglidir; govde ve tel # arasindaki oran bagli degildir. node kenar/sunucu.mjs & SUNUCU=$! until curl -sf http://127.0.0.1:8472/sayac >/dev/null; do sleep 0.2; done olc() { curl -s -o /dev/null -D - "$1" -w '%{size_download} %{size_header}\n' 2>/dev/null; } printf '%-18s %-7s %6s %7s %5s\n' istek kenar govde baslik tel olc http://127.0.0.1:8471/takip/TR900010 | awk 'END{printf "%-18s %-7s %6d %7d %5d\n", "koken dogrudan", "-", $1, $2, $1+$2}' for tur in 1 2 3; do for no in TR900011 TR900012; do olc "http://127.0.0.1:8472/takip/$no" | awk -v n="$no" -v t="$tur" '/^x-kenar/{k=$2; sub(/\r/, "", k)} END{printf "%-18s %-7s %6d %7d %5d\n", "kenar " n " t" t, k, $1, $2, $1+$2}' done done printf '\nkenar sayaci = %s\n' "$(curl -s http://127.0.0.1:8472/sayac)" printf 'koken sayaci = %s\n' "$(curl -s http://127.0.0.1:8471/sayac)" kill $SUNUCU
istek kenar govde baslik tel
koken dogrudan - 480 156 636
kenar TR900011 t1 iska 480 171 651
kenar TR900012 t1 iska 480 171 651
kenar TR900011 t2 isabet 480 173 653
kenar TR900012 t2 isabet 480 173 653
kenar TR900011 t3 isabet 480 173 653
kenar TR900012 t3 isabet 480 173 653
kenar sayaci = {"istek":6,"isabet":4,"iska":2,"kokenIstek":2,"kokenBayt":960}
koken sayaci = {"istek":3,"isabet":0,"depo":3}
Sayılar ölçüm sınıfındadır. Başlık baytları sunucunun ürettiği başlık kümesine bağlıdır ve başka bir uygulamada başka çıkar; gövde ile tel arasındaki oran ise 653 / 480 = 1,360’tır ve bu oran bir alt sınır olarak taşınabilir, çünkü başlık kümesi ancak büyür.
Sayaçlar konum kararının ilk sonucunu veriyor. Kenar altı istek gördü, ikisi ıska, dördü isabet.
Kökene giden istek sayısı ikidir: aynı iki takip numarasının ikinci ve üçüncü sorgusu kökene hiç
ulaşmadı. Kökenin kendi sayacı üç istek gösteriyor, çünkü doğrudan yapılan ilk ölçüm de sayıldı.
Kökenin isabet sayacı sıfırdır — kenarın arkasında kalan istekler öyle seyrekleşmiştir ki
kökenin kendi önbelleği hiç işe yaramamıştır. Bu, dersin ikinci yarısında ölçülecek olan sonucun
küçük bir örneğidir.
İsabet Oranı Nereden Gelir
İkinci düzenek bir süreç içi modeldir ve model olduğu belirtilir: aynı istek izi önce tek bir önbellekle, sonra önüne kenar önbelleği konarak koşturulur. İz bu kursun varsayımıdır, oranlar ve sayımlar ölçümdür.
Modelin girdisi giriş kursundan türetilir. Günde 12.000.000 takip sorgusu ve günde 400.000 yeni gönderi vardı. Buraya bir varsayım eklenir — T6: bir gönderinin yolda kaldığı gün 3 — ve etkin gönderi sayısı 1.200.000 çıkar; gönderi başına günlük sorgu 12.000.000 / 1.200.000 = 10 olur. Bu sayı hesap sınıfındadır.
On sorgu bir gönderinin üç günlük ömrüne yayılmıştır. Bir önbellek girdisinin üç gün yaşaması beklenemez, dolayısıyla oranı belirleyen şey on sorgunun kaçının aynı kısa pencerede geldiğidir: kullanıcı takip sayfasını açar ve birkaç kez yeniler. Bu, ikinci varsayımdır — T7: öbek uzunluğu, yani on sorgudan kaçının aynı öbekte geldiği. Üçüncüsü T8: aynı anda açık öbek sayısı 100, dördüncüsü T9: her önbelleğin kapasitesi 1.000 anahtar.
// kenar/iz.mjs — surec ici model: ayni istek izi once tek onbellekle, sonra onune kenar // onbellegi konarak kosturulur. Iz bir varsayimdir (T7), oranlar ve sayimlar olcumdur. const ISTEK = 200_000; // K01 tepe okuma 416,67 istek/s x 480 sn const SORGU = 10; // hesap: V1*V2 / (V3*T6) = 12.000.000 / 1.200.000 const ACIK = 100; // T8: ayni anda acik takip sayfasi (obek) sayisi const KAPASITE = 1_000; // T9: her onbellegin anahtar kapasitesi function* iz(obek) { // obek: bir gonderinin 10 sorgusundan kaci ayni obekte geliyor const acik = []; let yeni = 1, tek = 1, dizin = 0; for (let i = 0; i < ISTEK; i++) { if (i % SORGU >= obek) { yield `D${tek++}`; continue; } // pencerede bir kez gecen anahtar while (acik.length < ACIK) acik.push({ ad: `G${yeni++}`, kalan: obek }); dizin = (dizin + 1) % acik.length; const o = acik[dizin]; yield o.ad; if (--o.kalan === 0) acik.splice(dizin, 1); } } class Onbellek { // en son kullanilan sonda durur; Map'in ekleme sirasi yeter constructor(k) { this.k = k; this.m = new Map(); this.isabet = 0; this.iska = 0; } ara(a) { if (this.m.has(a)) { this.isabet += 1; this.m.delete(a); this.m.set(a, 1); return true; } this.iska += 1; if (this.m.size >= this.k) this.m.delete(this.m.keys().next().value); this.m.set(a, 1); return false; } get oran() { return this.isabet / (this.isabet + this.iska); } } export function kosum(obek) { const tek = new Onbellek(KAPASITE); const kenar = new Onbellek(KAPASITE), uygulama = new Onbellek(KAPASITE); for (const a of iz(obek)) { tek.ara(a); if (!kenar.ara(a)) uygulama.ara(a); } return { istek: ISTEK, tekOran: tek.oran, tekDepo: tek.iska, kenarOran: kenar.oran, uygulamaIstek: kenar.iska, uygulamaOran: uygulama.oran, kenarliDepo: uygulama.iska }; } if (process.argv[1].endsWith("iz.mjs")) { // iceri alindiginda basmaz, yalniz kosum'u verir console.log(`${ISTEK} istek, gonderi basina ${SORGU} sorgu, ${ACIK} acik obek, ` + `onbellek kapasitesi ${KAPASITE} anahtar`); console.log(); console.log("obek | tek onbellek | kenar isabet | uygulamaya ulasan | uyg. isabet | depoya ulasan"); console.log("-----|--------------|--------------|-------------------|-------------|--------------"); for (const obek of [10, 5, 2]) { const r = kosum(obek); console.log(`${String(obek).padStart(4)} | ${r.tekOran.toFixed(4).padStart(12)} | ` + `${r.kenarOran.toFixed(4).padStart(12)} | ${String(r.uygulamaIstek).padStart(17)} | ` + `${r.uygulamaOran.toFixed(4).padStart(11)} | ${String(r.kenarliDepo).padStart(13)}`); } const r = kosum(10); console.log(); console.log(`obek 10: depoya ulasan istek kenarsiz ${r.tekDepo}, kenarli ${r.kenarliDepo} ` + `(x${(r.kenarliDepo / r.tekDepo).toFixed(3)})`); console.log(`uygulamaya ulasan istek ${r.istek} -> ${r.uygulamaIstek} ` + `(x${(r.uygulamaIstek / r.istek).toFixed(3)})`); console.log(`olculen kenar isabeti ${r.kenarOran.toFixed(4)}, V9 varsayimi 0,90 -> fark ` + `${(Math.abs(r.kenarOran - 0.9) * 100).toFixed(2)} puan`); }
200000 istek, gonderi basina 10 sorgu, 100 acik obek, onbellek kapasitesi 1000 anahtar obek | tek onbellek | kenar isabet | uygulamaya ulasan | uyg. isabet | depoya ulasan -----|--------------|--------------|-------------------|-------------|-------------- 10 | 0.8998 | 0.8998 | 20046 | 0.0000 | 20046 5 | 0.3998 | 0.3998 | 120044 | 0.0000 | 120044 2 | 0.0759 | 0.0759 | 184815 | 0.0021 | 184418 obek 10: depoya ulasan istek kenarsiz 20046, kenarli 20046 (x1.000) uygulamaya ulasan istek 200000 -> 20046 (x0.100) olculen kenar isabeti 0.8998, V9 varsayimi 0,90 -> fark 0.02 puan
İlk satır giriş kursunun bir varsayımını açıyor. Önbellekten karşılanma oranı orada V9 = 0,90 olarak seçilmişti ve gerekçesi “aynı takip numarası kısa aralıkla yeniden sorulur” diye bir cümleydi. Öbek uzunluğu 10 alındığında ölçülen oran 0,8998’dir: fark 0,02 puan. Yani V9 = 0,90 gizlice şunu varsayıyordu: bir gönderinin on sorgusunun tamamı aynı öbekte geliyor. Varsayımın duyarlılığı ikinci ve üçüncü satırda: öbek 5 olduğunda oran 0,3998, öbek 2 olduğunda 0,0759. Sorguların yarısı öbek dışına düşerse oran 0,90’dan 0,40’a iner. Bir isabet oranı bir önbellek niteliği değil, iz niteliğidir.
İkinci sonuç konum kararının asıl bulgusudur. Kenar önbelleği eklendiğinde uygulamaya ulaşan istek 200.000’den 20.046’ya, onda birine düştü. Buna karşılık depoya ulaşan istek hiç değişmedi: kenarsız 20.046, kenarlı 20.046, oran 1,000. Sebebi uygulama önbelleğinin isabet oranıdır: 0,0000. Kenar önbelleği tekrarların tamamını kendine aldığı için arkasındaki önbelleğe yalnız ilk kez görülen anahtarlar kalıyor ve ilk kez görülen bir anahtar hiçbir önbellekte bulunamaz.
Bu sonuç iki önbelleğin seri bağlanmasının genel bir özelliğidir: son önbellek pencerenin bütün ayrı anahtarlarını tutabiliyorsa, depoya ulaşan istek sayısı önündeki önbelleklerden bağımsızdır. Aynı satırların üçüncüsü sınırı gösteriyor: öbek 2 olduğunda kenar önbelleği zaten başarısızdır (0,0759) ve arkadaki önbellek küçük bir katkı verebiliyor (0,0021), depoya ulaşan istek 184.815’ten 184.418’e iniyor. İkinci önbellek ancak birincisi işini yapmadığında bir şey kazandırıyor. Kapasite ile isabet oranı arasındaki ilişki Önbellek Ölçütleri dersinde ölçülmüştü; burada eklenen, aynı anahtarı iki kez önbelleklemenin ikinci kez bedava olmadığıdır.
Hesaba Dönüş
// kenar/hesap.mjs — olculen oran ve tel baytlarinin K01 hesaplarina geri konmasi import { kosum } from "./iz.mjs"; const TEPE_OKUMA = 416.67, TEPE_YAZMA = 97.22; // K01 hesabi const V5 = 480, V9 = 0.90; // K01 varsayim tablosu const K01_ARKA = 41.67, K01_DEPO = 138.89, K01_CIKIS = 1.60; const TEL_KENAR = 653, TEL_KOKEN = 636; // olcum: govde + baslik (yerel surecler) const r = kosum(10); const uygulama = TEPE_OKUMA * (1 - r.kenarOran); const depoOkuma = TEPE_OKUMA * (r.kenarliDepo / r.istek); const mbit = (istek, bayt) => (istek * bayt * 8) / 1e6; console.log(`olculen kenar isabeti = ${r.kenarOran.toFixed(4)} (V9 varsayimi ${V9})`); console.log(); console.log(`${"hesap".padEnd(26)}${"K01".padStart(10)}${"kenarli".padStart(10)} not`); const satir = (ad, once, sonra, not) => console.log(`${ad.padEnd(26)}${once.toFixed(2).padStart(10)}${sonra.toFixed(2).padStart(10)} ${not}`); satir("uygulamaya ulasan ok./s", TEPE_OKUMA, uygulama, "kenar onbellegi cikardi"); satir("onbellek arkasi okuma/s", K01_ARKA, depoOkuma, "degismedi"); satir("depoya ulasan istek/s", K01_DEPO, depoOkuma + TEPE_YAZMA, "degismedi"); satir("okuma cikis Mbit/s", K01_CIKIS, mbit(TEPE_OKUMA, TEL_KENAR), "K01 yalniz govde saydi"); satir("kokenden cikis Mbit/s", K01_CIKIS, mbit(uygulama, TEL_KOKEN), "kenar sinirin arkasinda"); console.log(); console.log(`tel/govde orani = ${(TEL_KENAR / V5).toFixed(3)}; K01'in 1,60 Mbit/s'i alt sinirdir`); console.log(`kokenden cikis / kullaniciya cikis = ` + `${(mbit(uygulama, TEL_KOKEN) / mbit(TEPE_OKUMA, TEL_KENAR)).toFixed(4)}`); console.log(`uygulama onbelleginin katkisi = ${(r.uygulamaOran * 100).toFixed(2)}% isabet`);
olculen kenar isabeti = 0.8998 (V9 varsayimi 0.9) hesap K01 kenarli not uygulamaya ulasan ok./s 416.67 41.76 kenar onbellegi cikardi onbellek arkasi okuma/s 41.67 41.76 degismedi depoya ulasan istek/s 138.89 138.98 degismedi okuma cikis Mbit/s 1.60 2.18 K01 yalniz govde saydi kokenden cikis Mbit/s 1.60 0.21 kenar sinirin arkasinda tel/govde orani = 1.360; K01'in 1,60 Mbit/s'i alt sinirdir kokenden cikis / kullaniciya cikis = 0.0976 uygulama onbelleginin katkisi = 0.00% isabet
Tablo dört sonucu bir arada veriyor. Uygulamaya ulaşan okuma hızı 416,67’den 41,76’ya iniyor; bu
sayı giriş kursunda hiç hesaplanmamıştı, çünkü orada önbellek uygulamanın içindeydi ve isteğin
uygulamaya varması kaçınılmazdı. onbellek arkasi okuma/s ile depoya ulasan istek/s
değişmiyor — 41,67’ye karşı 41,76 ve 138,89’a karşı 138,98; fark iz ölçümünün 0,02 puanlık
sapmasından geliyor. Kenar önbelleği aynı 0,90’ı bir yerden alıp başka bir yere koyuyor.
Bant genişliği iki yönde düzeltiliyor. Kullanıcıya giden çıkış, ölçülen tel baytıyla 1,60’tan 2,18 Mbit/s’e çıkıyor: giriş kursunun sayısı yalnız gövdeyi saydığı için bir alt sınırdı. Kökenden çıkan bant genişliği ise 0,21 Mbit/s’tir, kullanıcıya giden çıkışın 0,0976’sı. Aynı tasarımda bir bağ 2,18 Mbit/s, ötekisi 0,21 Mbit/s istiyor ve giriş kursunun tek sayısı ikisini de temsil edemiyor.
Özet
- Kenar önbelleği bir konum kararıdır: isabet edilen istek uygulamaya varmaz ve köken ile kenar arasındaki bağı kullanmaz, ama isabet oranı bu kararla büyümez.
- Aynı gövdenin gerçek bağlantıda tuttuğu yer ölçüldü: 480 baytlık gövde tel üzerinde 653 bayt tutuyor, oran 1,360; giriş kursunun 1,60 Mbit/s’i bu yüzden bir alt sınırdır.
- Öbek uzunluğu 10 alındığında ölçülen isabet 0,8998 çıktı: V9 = 0,90 varsayımı, bir gönderinin on sorgusunun tamamının aynı öbekte geldiğini varsayıyordu. Öbek 5 olduğunda oran 0,3998’e iner.
- Kenar önbelleği uygulamaya ulaşan isteği 200.000’den 20.046’ya indirdi, ama depoya ulaşan istek 20.046’da kaldı (oran 1,000) ve uygulama önbelleğinin isabeti 0,0000 oldu.
- Son önbellek pencerenin bütün ayrı anahtarlarını tutabiliyorsa depoya ulaşan istek önündeki önbelleklerden bağımsızdır; ikinci önbellek ancak birincisi başarısız olduğunda katkı verir.
- Hesaba dönüş: uygulamaya ulaşan okuma 416,67’den 41,76’ya indi,
depoya ulasan istek/s138,89 olarak kaldı, kökenden çıkış 0,21 Mbit/s ile kullanıcıya giden çıkışın 0,0976’sı oldu.
Sonraki Adım
Bu dersteki kenar önbelleği isteği bekledi: bir anahtar ilk kez sorulduğunda ıska verdi, kökene gitti ve yanıtı sakladı. Ölçüm o ilk isteklerin sayısını da verdi — 200.000 isteğin 20.046’sı kenarda ıska oldu ve her biri kökene bir gidiş demek. Bu bekleme zorunlu değildir: içerik sorulmadan önce de kenara gönderilebilir. İki model arasındaki seçim ıska sayısını, kenarda tutulan içerik hacmini ve bir güncellemenin ne kadar sürede her kenarda görünür olduğunu değiştirir. Sonraki ders iki modeli aynı düzenekte ölçer ve hangisinin hangi içerik türü için yerinde olduğunu gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.