Ders 06 / 19
Kenar Oluşturma
Oluşturmanın kullanıcıya coğrafi olarak yaklaştırılması; mesafenin gecikmeye çevrimi, kenar düğümünün aktarma, birleştirme ve tam üretim biçimleri, kenar çalışma zamanının kısıtları ve verinin nerede durduğu sorusu.
İçindekiler
Önceki iki teknik yanıtın gönderim biçimini ve etkileşimin kazanılma anını değiştirdi; sunucu ile kullanıcı arasındaki mesafeyi olduğu gibi bıraktı. İlk dersteki zincir hesabında ağ turu 60 milisaniye varsayılmış ve bu kalemin kod değişikliğiyle azalmadığı söylenmişti: sinyalin kat edeceği yol kısalmadıkça süre kısalmaz.
Bu ders yolu kısaltmayı ele alır: oluşturmanın bir bölümünün ya da tamamının, kullanıcıya yakın duran bir düğüme taşınması.
Mesafe Bir Alt Sınır Koyar
Işık cam lifte boşluktakinden yavaş, yaklaşık 200.000 kilometre/saniye hızla ilerler. Bu, hiçbir yazılımın aşamayacağı bir alt sınır verir.
// mesafe.mjs — cografi uzakligin gecikmeye cevrimi ve kenarin kazandirdigi turlar // Isik cam lifte yaklasik 200.000 km/s ile ilerler: bu bir alt sinirdir, // gercek yollar duz cizgi degildir ve her atlamada isleme suresi eklenir. const HIZ = 200000; // km/s const tur = (km) => (2 * km / HIZ) * 1000; // ms, gidis-donus console.log("mesafe (km)".padStart(12) + "tek yon (ms)".padStart(14) + "tur (ms)".padStart(11)); for (const km of [50, 500, 2500, 9000, 16000]) { console.log(String(km).padStart(12) + (tur(km) / 2).toFixed(1).padStart(14) + tur(km).toFixed(1).padStart(11)); } // Bir sayfanin ilk gorunume kadar odedigi tur sayisi: // 1 tasima el sikismasi + 1 sifreleme el sikismasi + 1 belge istegi + 1 alt kaynak turu. const TUR_SAYISI = 4; const SUNUCU_ISI = 40; // ms, veri kaynagi + sablon const KENAR_ISI = 2; // ms, kenar dugumunun kendi isi const KULLANICI_KENAR = 50; // km const KULLANICI_KAYNAK = 9000; // km const yalnizKaynak = TUR_SAYISI * tur(KULLANICI_KAYNAK) + SUNUCU_ISI; const kenarIsabet = TUR_SAYISI * tur(KULLANICI_KENAR) + KENAR_ISI; const kenarIska = kenarIsabet + tur(KULLANICI_KAYNAK - KULLANICI_KENAR) + SUNUCU_ISI; console.log("\nsenaryo".padEnd(34) + "sure (ms)".padStart(11) + "kaynaga gore".padStart(14)); for (const [ad, sure] of [ ["yalniz kaynak sunucu", yalnizKaynak], ["kenar, kopyasi var", kenarIsabet], ["kenar, kaynaga sormasi gerekti", kenarIska], ]) { console.log(ad.padEnd(34) + sure.toFixed(1).padStart(11) + (sure / yalnizKaynak).toFixed(2).padStart(14)); }
mesafe (km) tek yon (ms) tur (ms)
50 0.3 0.5
500 2.5 5.0
2500 12.5 25.0
9000 45.0 90.0
16000 80.0 160.0
senaryo sure (ms) kaynaga gore
yalniz kaynak sunucu 400.0 1.00
kenar, kopyasi var 4.0 0.01
kenar, kaynaga sormasi gerekti 133.5 0.33
Üst tablo tek başına bir tasarım kısıtıdır: dokuz bin kilometre uzaktaki bir sunucuya tek bir gidiş-dönüş 90 milisaniyeden aşağı inemez. Bu sayı iyimserdir; gerçek yollar düz çizgi değildir ve her ara düğümde işleme süresi eklenir.
Alt tablo turların çarpan etkisini gösterir. Sayfanın görünmesi dört tur istiyorsa uzak sunucuda bu 360 milisaniyeye çıkar ve veri kaynağının 40 milisaniyelik işi toplamın onda birinden azına düşer. Sunucunun işini hızlandırmak burada boşa çabadır; azaltılması gereken şey turların uzunluğudur.
Üçüncü satır kenarın sınırını da söyler: kenar düğümü isteği karşılayamayıp kaynağa sormak zorunda kalırsa el sıkışmalar yakınlaşır ama içerik yine uzaktan gelir ve süre kaynağın üçte birine iner, yüzde birine değil.
Kenar Düğümünün Üç İş Biçimi
Kenarda bir düğüm bulunması tek başına bir şey söylemez; belirleyici olan o düğümün ne yaptığıdır. Üç biçim ayrı ayrı ölçülebilir. Aşağıdaki kaynak sunucuda coğrafi uzaklık, yanıttan önce eklenen sabit bir gecikmeyle modellenmiştir; gerçek bir ağ ölçümü değildir.
// kaynak-sunucu.mjs — uzaktaki kaynak sunucu // Cografi uzaklik, yanittan once eklenen sabit bir gecikmeyle modellenmistir. import { createServer } from "node:http"; const UZAKLIK = 80; // ms, kaynaga gidip donmenin bedeli const VERI_ISI = 40; // ms, veri kaynagindan okuma const bekle = (ms) => new Promise((c) => setTimeout(c, ms)); const KABUK_ICI = '<h1>Kuzey Yamac Olcum Istasyonu</h1>' + '<ul><li>06:00 -4.2 C</li><li>07:00 -3.8 C</li><li>08:00 -2.1 C</li></ul>'; const gonder = (yanit, govde) => { yanit.setHeader("Content-Type", "text/html; charset=utf-8"); yanit.setHeader("Content-Length", Buffer.byteLength(govde)); yanit.writeHead(200).end(govde); }; createServer(async (istek, yanit) => { yanit.sendDate = false; await bekle(UZAKLIK); if (istek.url === "/sayfa") { await bekle(VERI_ISI); gonder(yanit, `<!doctype html><html lang="tr"><body>${KABUK_ICI}` + `<p>Sorumlu: (kaynakta uretildi)</p></body></html>\n`); } else if (istek.url === "/parca/kisisel") { await bekle(5); // kucuk, kisiye ozel parca gonder(yanit, "<p>Sorumlu: A. Yilmaz</p>"); } else if (istek.url === "/veri/olcumler") { await bekle(VERI_ISI); yanit.setHeader("Content-Type", "application/json"); const govde = JSON.stringify([ { zaman: "06:00", sicaklik: -4.2 }, { zaman: "07:00", sicaklik: -3.8 }, { zaman: "08:00", sicaklik: -2.1 }, ]); yanit.setHeader("Content-Length", Buffer.byteLength(govde)); yanit.writeHead(200).end(govde); } else { yanit.writeHead(404).end(); } }).listen(8176, "127.0.0.1", () => console.log("kaynak: 127.0.0.1:8176"));
// kenar-sunucu.mjs — kullaniciya yakin, kod calistirabilen kenar dugumu // Kaynak sunucuya erisim 8176 uzerinden; kullaniciya olan uzaklik ihmal edilir. import { createServer } from "node:http"; const KAYNAK = "http://127.0.0.1:8176"; // Kenarin elindeki kopyalar: kabuk derleme ciktisindan, olcumler kaynaktan gelir. const KABUK = (icerik, kisisel) => `<!doctype html><html lang="tr"><body>${icerik}${kisisel}</body></html>\n`; const BASLIK = "<h1>Kuzey Yamac Olcum Istasyonu</h1>"; let olcumKopyasi = null; const gonder = (yanit, govde) => { yanit.setHeader("Content-Type", "text/html; charset=utf-8"); yanit.setHeader("Content-Length", Buffer.byteLength(govde)); yanit.writeHead(200).end(govde); }; const liste = (olcumler) => "<ul>" + olcumler.map((o) => `<li>${o.zaman} ${o.sicaklik} C</li>`).join("") + "</ul>"; createServer(async (istek, yanit) => { yanit.sendDate = false; if (istek.url === "/gecis") { // Kenar yalnizca aktarir: butun is kaynakta yapilir. const kaynakYaniti = await fetch(`${KAYNAK}/sayfa`); gonder(yanit, await kaynakYaniti.text()); } else if (istek.url === "/birlestir") { // Kabuk kenarda hazir; yalnizca kisiye ozel kucuk parca kaynaktan istenir. const parca = await (await fetch(`${KAYNAK}/parca/kisisel`)).text(); gonder(yanit, KABUK(BASLIK + liste(olcumKopyasi ?? []), parca)); } else if (istek.url === "/kenarda") { // Sayfanin tamami kenardaki kopyadan uretilir; kaynak hic sorulmaz. gonder(yanit, KABUK(BASLIK + liste(olcumKopyasi ?? []), "<p>Sorumlu: (genel)</p>")); } else if (istek.url === "/tazele") { // Kenarin veri kopyasini kaynaktan yeniler. olcumKopyasi = await (await fetch(`${KAYNAK}/veri/olcumler`)).json(); gonder(yanit, `<p>${olcumKopyasi.length} olcum alindi</p>\n`); } else { yanit.writeHead(404).end(); } }).listen(8177, "127.0.0.1", () => console.log("kenar: 127.0.0.1:8177"));
Aşağıdaki betik bu iki dosyayla aynı dizinde çalıştırılır. Bağlantı noktaları 8176 ve 8177 keyfîdir ve boş olmalıdır; kullanımdaysa üç dosyada da değiştirilir.
#!/usr/bin/env bash # Iki sunucuyu baslatir, uc is bolumunu olcer, sonra durdurur. node kaynak-sunucu.mjs > /dev/null & kaynak=$! node kenar-sunucu.mjs > /dev/null & kenar=$! sleep 1 BICIM='ilk bayt %{time_starttransfer} s govde %{size_download} B\n' printf '%-38s' "0) kenarin kopyasini tazelemesi" curl -sS -o /dev/null -w "$BICIM" http://127.0.0.1:8177/tazele printf '%-38s' "1) dogrudan kaynak sunucu" curl -sS -o /dev/null -w "$BICIM" http://127.0.0.1:8176/sayfa printf '%-38s' "2) kenar yalnizca aktariyor" curl -sS -o /dev/null -w "$BICIM" http://127.0.0.1:8177/gecis printf '%-38s' "3) kenar birlestiriyor" curl -sS -o /dev/null -w "$BICIM" http://127.0.0.1:8177/birlestir printf '%-38s' "4) kenar tumuyle kendisi uretiyor" curl -sS -o /dev/null -w "$BICIM" http://127.0.0.1:8177/kenarda echo "--- kenarda uretilen govde ---" curl -sS http://127.0.0.1:8177/kenarda kill "$kaynak" "$kenar"
0) kenarin kopyasini tazelemesi ilk bayt 0.154206 s govde 22 B 1) dogrudan kaynak sunucu ilk bayt 0.124900 s govde 195 B 2) kenar yalnizca aktariyor ilk bayt 0.129912 s govde 195 B 3) kenar birlestiriyor ilk bayt 0.094019 s govde 185 B 4) kenar tumuyle kendisi uretiyor ilk bayt 0.000815 s govde 183 B
Sıfırıncı satır ölçüme girmez; kenarın veri kopyasını doldurduğu hazırlık adımıdır. Süre alanları makineye bağlıdır ve yorumu oranlar taşır.
İkinci satır kenarın yanlış kullanımıdır. Yalnızca aktaran bir düğüm, kaynağın süresine kendi işini ekler ve doğrudan erişimden yavaştır — ölçülen çalıştırmada yaklaşık yüzde dört. Farkın büyüklüğü çalıştırmaya göre değişir, işareti değişmez: araya bir düğüm koymanın tek başına bir kazancı yoktur; kazanç, o düğümün üstlendiği işten gelir.
Üçüncü satır iş bölümüdür. Kabuk ve ölçüm listesi kenarda hazırdır; kaynaktan yalnızca kişiye özel küçük parça istenir. Süre, doğrudan erişimin yaklaşık dörtte üçüne iner. Kazanç kaynağa hiç gitmemekten değil, kaynaktan az şey istemekten gelir.
Dördüncü satır tam kenarda üretimdir. Kaynak hiç sorulmaz; süre iki mertebe kısalır — ölçülen çalıştırmada yaklaşık yüz elli kat. Bunun koşulu, sayfayı üretmek için gereken her şeyin kenarda bulunmasıdır.
Kenar Çalışma Zamanının Kısıtları
Kenarda kod çalıştırmak, kaynak sunucuda kod çalıştırmakla aynı şey değildir. Kısıtlar kenarın tanımından doğar: çok sayıda düğümde, her düğümde az kaynakla, çok sayıda kiracı yan yana çalışır.
Süreç uzun yaşamaz. İstek dışında ayakta duran bir bellek durumu varsayılamaz; bir düğümde tutulan sayaç başka bir düğümde yoktur. Durum, ya isteğin kendisinde ya paylaşımlı bir depoda tutulur.
İşlem ve bellek bütçesi dardır. Ağır bir şablon işleme, büyük bir bağımlılık ağacı ya da uzun süren bir hesap kenara uygun değildir. Kenarda çalışacak kod, kaynak sunucuda çalışan koddan küçüktür ve bu bir tercih değil bir sınırdır.
İlk çağrının maliyeti ayrıdır. Uzun süre çağrılmamış bir kod parçası yeniden yüklenirken bir başlatma süresi doğar. Kenarın kazandırdığı milisaniyeler bu süreyle aynı mertebede olabilir; bu yüzden nadiren çağrılan yollar kenara taşınarak hızlanmaz.
Ortam düğümden düğüme aynı değildir. Kenarda çalışan kod, kaynak sunucudakinin tüm yeteneklerine sahip olmayabilir. Dosya sistemi, uzun ömürlü bağlantılar ve kimi kitaplıklar bulunmayabilir; kod bu varsayımla yazılır.
Asıl Soru Verinin Nerede Olduğudur
Ölçümün dördüncü satırındaki kazanç, kenarın ölçüm listesinin bir kopyasını taşımasından doğdu. Bu kopya sıfırıncı adımda kaynaktan alınmıştı ve alındığı andan itibaren eskimeye başlar.
Buradan kenar oluşturmanın gerçek kuralı çıkar: oluşturma, verisinin yanına taşınabildiği kadar taşınabilir. Kenara taşınan kod, kaynaktaki veriyi her istekte soruyorsa mesafe yeniden ödenir; üçüncü satır bu durumun ölçüsüdür. Kazancın tamamı ancak veri de kenarda olduğunda alınır.
Verinin kenara taşınması üç biçimde olur ve üçü de bir bedel taşır. Nadiren değişen veri kopyalanır; bedeli tazeliktir ve artımlı yeniden üretim dersindeki pencere hesabı burada da geçerlidir. Kullanıcıya bağlı küçük veri isteğin kendisinde taşınır; bedeli, bu verinin kurcalanamayacak biçimde imzalanması gereğidir. Geri kalan her şey kaynakta kalır; bedeli, o bölümler için mesafenin ödenmesidir.
Bu üçlü ayrım, sayfanın bölümlere ayrılmasını gerektirir. İstasyon sayfasında kabuk ve ölçüm listesi kopyalanabilir, sorumlu adı istekte taşınabilir, düzenleme yetkileri kaynakta sınanır. Sayfanın tek parça olduğu bir tasarımda bu ayrım yapılamaz ve sayfanın tamamı en kısıtlayıcı bölümün kuralına uyar.
Özet
- Coğrafi mesafe gecikmeye bir alt sınır koyar: dokuz bin kilometre için tek gidiş-dönüş 90 milisaniyeden aşağı inmez ve sayfa başına ödenen tur sayısıyla çarpılır.
- Kenarda bir düğüm bulunması tek başına kazanç değildir; yalnızca aktaran bir düğüm doğrudan erişimden yavaştır.
- Kaynaktan yalnızca küçük bir parça isteyen kenar, süreyi doğrudan erişimin dörtte üçüne; hiç sormayan kenar ise yüzde birinin altına indirir.
- Kenar çalışma zamanı uzun ömürlü bellek durumu varsaymaz, dar bir işlem ve bellek bütçesiyle çalışır, ilk çağrıda bir başlatma süresi doğurur ve yetenekleri kaynak sunucununkinden dardır.
- Oluşturma ancak verisinin taşınabildiği kadar kenara taşınabilir; sayfanın bölümlere ayrılmadığı bir tasarımda tamamı en kısıtlayıcı bölümün kuralına uyar.
Sonraki Adım
Altı model ayrı ayrı ölçüldü ve her biri belirli bir durumda üstün çıktı. Geriye bu ölçümleri tek bir karara bağlamak kalıyor. Kuzey Yamaç sitesinde yedi ayrı sayfa türü vardır ve hepsinin aynı modelle üretilmesi için hiçbir neden yoktur: tanıtım sayfası değişmez, ölçüm listesi dakikada bir değişir, arşiv sayfaları binlercedir, panel kişiye özeldir. Sonraki ders bu envanteri üç eksene — içerik tazeliği, kişiselleştirme ve ölçek — oturtur ve her sayfa için modeli bu eksenlerden türeten bir karar kuralı kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.