Ders 18 / 24
Önbellek Stratejileri
Tarayıcıdaki iki önbellek katmanının iş bölümü, başlıklarla verilen tazelik kararı ve koşullu istek, önbellek anahtarının üretimi, dört stratejinin aynı durumlarda verdiği sonuç ve sürümleme ile temizlik.
İçindekiler
Önceki ders araya girme yetkisini kurdu, onunla ne yapılacağını açık bıraktı. İsteği karşılamanın tek bir doğru biçimi yoktur: istasyonun logosu ile ölçüm listesi aynı kuralla sunulamaz. Biri değişmez ve ağdan sorulması gereksizdir; diğeri dakikada bir değişir ve eski kopyası yanıltıcıdır.
Bu ders, kaynak başına verilen bu kararı iki soruya indirir: bir kopya nereden bulunur ve ne zaman taze sayılır.
İki Ayrı Önbellek Katmanı
Tarayıcıda iki ayrı önbellek vardır ve karıştırılmaları yanlış tanılara yol açar.
HTTP önbelleği, tarayıcının kendi yönettiği ve program tarafından görülmeyen katmandır. Neyin ne kadar saklanacağını sunucunun gönderdiği başlıklar belirler; sayfa kodunun sözü geçmez. Bir kaydı silmenin ya da içeriğini okumanın program yolu yoktur.
Program denetimli önbellek, servis çalışanının açıkça yazdığı ve sildiği ad–kayıt deposudur. Ne saklanacağına, ne kadar tutulacağına ve ne zaman sunulacağına yazan kod karar verir; başlıklar bu katmanda kendiliğinden hiçbir şey yapmaz.
İkisi bir zincirde durur. Servis çalışanı ağa çıktığında istek HTTP önbelleğinden geçer. Bu, ikili önbellekleme üretir: aynı kaynak iki katmanda da saklanabilir ve program katmanına yazılan kopya, HTTP katmanının eski kopyası olabilir. Kaçınmanın yolu, kritik kaynakları önbelleğe alırken isteği HTTP katmanını atlayacak biçimde kurmaktır.
Tazelik Kararı Nerede Verilir
HTTP katmanının kararı üç başlığa dayanır: saklama izni, ömür ve doğrulayıcı. Bunlar bir yanıtın başlıklarında okunabilir.
// sunucu.mjs — uc kaynagi uc ayri tazelik politikasiyla sunan yerel sunucu import { createServer } from "node:http"; const KAYNAKLAR = { "/logo.svg": { tip: "image/svg+xml", etiket: '"logo-3f2a"', denetim: "public, max-age=31536000, immutable", govde: '<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"></svg>', }, "/olcumler.json": { tip: "application/json", etiket: '"olcum-7"', denetim: "no-cache", govde: '{"sicaklik":-4.2,"nem":72}', }, "/oturum": { tip: "text/plain; charset=utf-8", etiket: null, denetim: "no-store", govde: "kisiye ozel icerik", }, }; createServer((istek, yanit) => { yanit.sendDate = false; // ciktiyi belirlenimci kilmak icin: Date basligi yazilmaz const kaynak = KAYNAKLAR[istek.url]; if (!kaynak) { yanit.writeHead(404).end(); return; } yanit.setHeader("Cache-Control", kaynak.denetim); if (kaynak.etiket) yanit.setHeader("ETag", kaynak.etiket); // Kosullu istek: istemcinin elindeki etiket sunucudakiyle ayniysa govde gonderilmez. if (kaynak.etiket && istek.headers["if-none-match"] === kaynak.etiket) { yanit.writeHead(304).end(); return; } yanit.setHeader("Content-Type", kaynak.tip); yanit.setHeader("Content-Length", Buffer.byteLength(kaynak.govde)); yanit.writeHead(200).end(kaynak.govde); }).listen(8137, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8137"));
Aşağıdaki betik bu dosyayla aynı dizinde çalıştırılır. Bağlantı noktası olarak seçilen 8137 keyfîdir ve boş olmalıdır; kullanımdaysa iki dosyada da değiştirilir.
#!/usr/bin/env bash # sunucu.mjs'yi baslatir, uc kaynagin yanit basliklarini okur, sonra durdurur. node sunucu.mjs > /dev/null & sunucu=$! sleep 1 echo "--- /logo.svg (surumlenmis varlik) ---" curl -sS -D - -o /dev/null http://127.0.0.1:8137/logo.svg echo "--- /olcumler.json (her kullanimda dogrulanir) ---" curl -sS -D - -o /dev/null http://127.0.0.1:8137/olcumler.json echo '--- /olcumler.json + If-None-Match: "olcum-7" ---' curl -sS -D - -o /dev/null -H 'If-None-Match: "olcum-7"' http://127.0.0.1:8137/olcumler.json echo "--- /oturum (hic saklanmaz) ---" curl -sS -D - -o /dev/null http://127.0.0.1:8137/oturum kill "$sunucu"
--- /logo.svg (surumlenmis varlik) --- HTTP/1.1 200 OK Cache-Control: public, max-age=31536000, immutable ETag: "logo-3f2a" Content-Type: image/svg+xml Content-Length: 67 Connection: keep-alive Keep-Alive: timeout=5 --- /olcumler.json (her kullanimda dogrulanir) --- HTTP/1.1 200 OK Cache-Control: no-cache ETag: "olcum-7" Content-Type: application/json Content-Length: 26 Connection: keep-alive Keep-Alive: timeout=5 --- /olcumler.json + If-None-Match: "olcum-7" --- HTTP/1.1 304 Not Modified Cache-Control: no-cache ETag: "olcum-7" Connection: keep-alive Keep-Alive: timeout=5 --- /oturum (hic saklanmaz) --- HTTP/1.1 200 OK Cache-Control: no-store Content-Type: text/plain; charset=utf-8 Content-Length: 18 Connection: keep-alive Keep-Alive: timeout=5
Bağlantıya ilişkin iki başlık taşıyıcı katmandan gelir ve tazelikle ilgisi yoktur; Date
başlığı çıktının her çalıştırmada aynı olması için bilerek kapatılmıştır, gerçek bir
sunucuda bulunur.
Üç politika üç ayrı kaynak sınıfını temsil eder. Adı sürümlenmiş bir varlık uzun ömür alır ve içeriği hiç değişmeyeceği bildirilir; böyle bir kaynak için doğrulama isteği bile gereksizdir. Sık değişen veri saklanabilir ama her kullanımda doğrulanmalıdır — başlığın adı yanıltıcıdır, saklamayı değil doğrulamasız kullanmayı yasaklar. Kişiye özel içerik hiçbir katmanda saklanmaz.
Üçüncü isteğin yanıtı doğrulamanın kazancını gösterir: istemci elindeki doğrulayıcıyı gönderir, sunucu içerik değişmediğini gövdesiz bir yanıtla bildirir. Ağ turu yapılmıştır ama gövde aktarılmamıştır. Bu, gecikmeyi ortadan kaldırmaz, bant genişliğini kurtarır; kopan bir bağlantıda doğrulama da yapılamayacağı için tek başına çevrimdışı davranış sağlamaz.
Önbellek Anahtarı
Program denetimli önbellekte bir kaydın anahtarı bir istektir, ve eşleme öntanımlı olarak adresin tamamı üzerinden yapılır. Bunun üç sonucu vardır.
Birincisi, sorgu dizgisi anahtarın parçasıdır. /olcumler?tur=sicaklik ile
/olcumler?tur=nem iki ayrı kayıttır. Aynı kaynağa yönlendirdiği bilinen sorgu
parametreleri varsa eşlemede sorgunun göz ardı edilmesi istenebilir; bu, izleme
parametreleri yüzünden aynı sayfanın onlarca kez saklanmasını önler.
İkincisi, aynı adresin farklı yöntemlerle yapılan istekleri ayrılabilir; önbellek öntanımlı olarak yalnızca güvenli yöntemleri saklamaya elverişlidir. Gövdeli bir isteğin yanıtını adres anahtarıyla saklamak, farklı gövdelerin aynı kayda düşmesi demektir.
Üçüncüsü, sunucu bir yanıtın hangi istek başlığına göre değiştiğini bildirebilir. Dil tercihine ya da kabul edilen içerik biçimine göre farklı yanıt veren bir uç için bu bildirim yapılmazsa, bir kullanıcının aldığı yanıt diğerine sunulur. Eşleme sırasında bu bildirimin dikkate alınıp alınmayacağı da seçilebilir.
Anahtar üretimini kendi eline alan bir çalışan, adresi yazmadan önce normalleştirir: izleme parametrelerini atar, kalan parametreleri sıraya koyar, adres parçasını siler. Aynı içeriğin tek anahtara düşmesi, hem yeri hem de isabet oranını iyileştirir.
Dört Strateji ve Verdikleri Söz
İsteği karşılamanın dört temel biçimi vardır. Aralarındaki fark, aynı üç durumda ne sundukları ve ağı ne zaman kullandıklarıyla görünür hâle gelir.
// strateji.mjs — dort onbellek stratejisinin ayni uc durumda verdigi sonuc const ADRES = "/istasyon/olcumler.json"; function kur({ onbelleklenmis, cevrimici }) { const durum = { agCagrisi: 0, onbellek: new Map() }; if (onbelleklenmis) durum.onbellek.set(ADRES, "v1"); durum.ag = async () => { durum.agCagrisi += 1; if (!cevrimici) throw new Error("ag yok"); return "v2"; }; return durum; } async function onbellekOnce(d) { const kayit = d.onbellek.get(ADRES); if (kayit) return kayit; const yanit = await d.ag(); d.onbellek.set(ADRES, yanit); return yanit; } async function agOnce(d) { try { const yanit = await d.ag(); d.onbellek.set(ADRES, yanit); return yanit; } catch { return d.onbellek.get(ADRES) ?? "(yedek sayfa)"; } } async function yenidenDogrulayarakSun(d) { const kayit = d.onbellek.get(ADRES); const tazeleme = d.ag().then((yanit) => d.onbellek.set(ADRES, yanit)).catch(() => {}); if (kayit) return kayit; // bekletmeden eski kopya verilir await tazeleme; return d.onbellek.get(ADRES) ?? "(yedek sayfa)"; } async function yalnizAg(d) { try { return await d.ag(); } catch { return "(yedek sayfa)"; } } const stratejiler = [ ["onbellek once", onbellekOnce], ["ag once", agOnce], ["yeniden dogrulayarak sun", yenidenDogrulayarakSun], ["yalniz ag", yalnizAg], ]; const durumlar = [ ["onbellek bos, cevrimici", { onbelleklenmis: false, cevrimici: true }], ["onbellekte v1, cevrimici", { onbelleklenmis: true, cevrimici: true }], ["onbellekte v1, cevrimdisi", { onbelleklenmis: true, cevrimici: false }], ]; console.log("strateji".padEnd(26) + "durum".padEnd(28) + "sunulan".padEnd(16) + "ag onbellekte kalan"); for (const [stratejiAdi, calistir] of stratejiler) { for (const [durumAdi, secenek] of durumlar) { const d = kur(secenek); const sunulan = await calistir(d); await new Promise((c) => setImmediate(c)); // arka plandaki tazeleme bitsin console.log( stratejiAdi.padEnd(26) + durumAdi.padEnd(28) + String(sunulan).padEnd(16) + String(d.agCagrisi).padEnd(4) + (d.onbellek.get(ADRES) ?? "(bos)")); } }
strateji durum sunulan ag onbellekte kalan onbellek once onbellek bos, cevrimici v2 1 v2 onbellek once onbellekte v1, cevrimici v1 0 v1 onbellek once onbellekte v1, cevrimdisi v1 0 v1 ag once onbellek bos, cevrimici v2 1 v2 ag once onbellekte v1, cevrimici v2 1 v2 ag once onbellekte v1, cevrimdisi v1 1 v1 yeniden dogrulayarak sun onbellek bos, cevrimici v2 1 v2 yeniden dogrulayarak sun onbellekte v1, cevrimici v1 1 v2 yeniden dogrulayarak sun onbellekte v1, cevrimdisi v1 1 v1 yalniz ag onbellek bos, cevrimici v2 1 (bos) yalniz ag onbellekte v1, cevrimici v2 1 v1 yalniz ag onbellekte v1, cevrimdisi (yedek sayfa) 1 v1
İkinci satır önbellek önce stratejisinin belirleyici özelliğidir: kopya varken ağ hiç kullanılmaz. Yanıt en hızlı biçimde gelir ve bağlantıdan bağımsızdır; bedeli, kaynak sunucuda değişse bile eski kopyanın süresiz sunulmasıdır. Bu yüzden yalnızca adı sürümlenmiş, içeriği asla değişmeyen varlıklar için doğrudur.
Beşinci ve altıncı satır ağ önce stratejisini özetler: çevrimiçiyken her zaman güncel, çevrimdışıyken elde ne varsa. Ağın yavaş olduğu durumda kullanıcı ağın gecikmesi kadar bekler; gerçek dağıtımlarda bu bekleme bir zaman aşımıyla sınırlanır ve süre dolunca kopyaya düşülür.
Sekizinci satır yeniden doğrulayarak sunma stratejisinin ödünleşimini tek satırda
gösterir: sunulan v1, önbellekte kalan v2. Kullanıcı beklemeden yanıt alır ama bir
sürüm geriden gelir; güncel içerik bir sonraki ziyarette görünür. Bu, tazeliğin bir tur
gecikmesine razı olunabilen içerik için doğru seçimdir ve arayüzün bunu üstü kapalı
bırakmaması gerekir.
Son üç satır yalnız ağ stratejisinin önbelleği hiç yazmadığını gösterir: üçüncü
durumda önbellekte v1 durduğu hâlde yedek sayfa sunulmuştur. Ölçüm gönderimi gibi
biriktirilemeyen istekler bu stratejiye aittir; çevrimdışıyken yapılacak şey eski veriyi
göstermek değil, isteğin gerçekleşmediğini bildirmektir.
Sürümleme, Temizlik ve Kota
Önbellekler adlandırılır ve ad sürüm taşır. Yeni bir çalışan sürümü kurulurken kendi adına yazar; etkinleşirken kendi adı dışındaki bütün önbellekleri siler. Bu iki adım birlikte, eski ve yeni varlıkların karışmasını önler: bir sürümün varlıkları ya tümüyle vardır ya tümüyle yoktur.
Kurulum sırasında toplanan kaynak listesi kısa tutulur. Uygulamanın açılması için gereken en küçük küme — belge kabuğu, temel biçem, temel betik, çevrimdışı yedek sayfa — kuruluma girer; geri kalan varlıklar kullanıldıkça yazılır. Kurulumun bir bütün olarak başarısız sayılması, uzun bir listenin tek bir kalemi yüzünden bütün dağıtımı engellemesi anlamına gelir.
Depolanan verinin sınırsız olmadığı da hesaba katılır. Tarayıcı köken başına bir üst sınır uygular ve yer daraldığında kökenin verisini bütün olarak silebilir. Buradan iki kural çıkar: önbellek kalıcı bir veritabanı gibi kullanılmaz, ve önbellekten okunan bir kaydın bulunamama olasılığı her zaman ele alınır.
Özet
- Tarayıcıda iki önbellek katmanı vardır: başlıklarla yönetilen HTTP önbelleği ve kodla yönetilen program denetimli önbellek; ikisi zincirde arka arkaya durur.
- Tazelik başlıkları üç kararı bildirir: saklanabilir mi, ne kadar taze sayılır, hangi doğrulayıcıyla sınanır. Doğrulama gövdesiz bir yanıtla sonuçlanır ve bant genişliğini kurtarır, gecikmeyi kaldırmaz.
- Önbellek anahtarı öntanımlı olarak adresin tamamıdır; sorgu dizgisi anahtarın parçasıdır ve normalleştirilmediğinde aynı içerik birden çok kayda düşer.
- Önbellek önce en hızlı ve en eskiye açık, ağ önce en taze ve en yavaş, yeniden doğrulayarak sunma hızlı ama bir sürüm geriden, yalnız ağ ise çevrimdışı davranışı olmayan stratejidir.
- Önbellek adları sürüm taşır; kurulumda yazılır, etkinleşmede eski adlar silinir. Kurulum listesi kısa tutulur ve depolama sınırlıdır.
Sonraki Adım
Bu ders sayfayı ağdan bağımsızlaştırdı: kaynaklar yerelde, kararlar tanımlı. Ortaya çıkan şey artık bir sayfadan fazlasıdır ve kullanıcıya da öyle görünmesi gerekir. Kuzey Yamaç sayfasını sahada kullanan kişi onu her seferinde adres yazarak açmak istemez; kendi penceresinde, kendi simgesiyle, tarayıcı arayüzü olmadan açılmasını bekler. Bunun için sayfanın kendini tanıtan bir bildirim dosyası, kurulabilirliğin ölçütleri ve arka plandan gelen bildirimlere ilişkin bir izin modeli gerekir. Sonraki ders bu üç parçayı ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.