Ders 11 / 19
Sağlık Uçları
Canlılık ile hazır olmanın kodda ayrı tutulması: iki ucun hangi soruyu yanıtladığı, bağımlılık denetiminin canlılık ucuna girmesinin ürettiği yeniden başlatma döngüsü ve aynı senaryonun iki kipte koşularak düşen istek ile toparlanma süresinin sayılması.
İçindekiler
Önceki derste kurulan her kural bir insanı çağırıyor ve insan dakikalar içinde yanıt veriyor. Bazı kararlar ise insana hiç sorulmaz. Ödünç sisteminin süreçlerinden birinin yaşayıp yaşamadığına ve trafik alıp almayacağına makine karar verir, saniyeler içinde ve günde binlerce kez. Bu karar iki ayrı soruya bakar; iki soru tek bir uca bağlandığında verilen yanıt yalnız yanlış olmaz, kendini besleyen bir döngü kurar.
Dayanıklılık ve Güvenilirlik kursu sağlık ucunun içeriğini ve denetim aralığını ölçtü; o tarama burada tekrarlanmaz. Buradaki soru tek: iki uç kodda ayrı mı, ve ayrılmadığında ne oluyor.
İki Soru, İki Eylem
Canlılık ucunun sorusu şudur: bu süreç ölmüş mü, ve yeniden başlatmak durumu düzeltir mi. Yanıtı olumsuzsa denetleyici tek bir eylem yapar — süreci öldürüp yeniden başlatır.
Hazır olma ucunun sorusu başkadır: bu süreç şu an iş alabilir mi. Yanıtı olumsuzsa eylem başkadır — trafik kesilir, ama süreç yaşamaya devam eder ve kendi kendine toparlanması beklenir.
Ayrımın kuralı bu iki eylemden çıkar: bir denetimin canlılık ucuna girebilmesi için, o denetim başarısız olduğunda yeniden başlatmanın çözüm olması gerekir. Sürecin kendi kilidi, kendi belleği, kendi olay döngüsü bu ölçüte uyar; süreç yeniden doğduğunda temizlenirler. Bir bağımlılık uymaz. Üyelik servisi erişilemezken ödünç sürecini yeniden başlatmak üyelik servisini geri getirmez; denetim yeni süreçte de başarısız olur, denetleyici onu da öldürür ve döngü bağımlılık dönene kadar sürer.
Düğüm: Ayrım İki Satırdadır
// saglik/dugum.mjs — odunc dugumu: iki saglik ucu (/canlilik, /hazir) ve iki is ucu. // Kip "ayri" ise canlilik yalniz surecin kendi durumunu okur; kip "birlesik" ise canlilik // uyelik servisine olan bagimliligi da denetler. Ayrimin tamami su iki satirdadir. import { createServer } from "node:http"; import { readFileSync } from "node:fs"; // SD1 (varsayim): yeniden baslayan surec yerel dizinini 24 istek boyunca yeniden kurar. export const ISINMA = 24; export function baslat(kip) { let islenen = 0, kilitli = false; const bagimli = () => readFileSync("bagimlilik.durum", "utf8").trim() === "acik"; const s = createServer((istek, yanit) => { let kod = 200; if (istek.url === "/canlilik") kod = kilitli || (kip === "birlesik" && !bagimli()) ? 500 : 200; else if (istek.url === "/hazir") kod = bagimli() && islenen >= ISINMA ? 200 : 503; else { islenen += 1; if (islenen <= ISINMA) kod = 503; // yerel dizin henuz kurulmadi else if (istek.url === "/odunc" && !bagimli()) kod = 503; // uyelik servisi gerekli } yanit.writeHead(kod).end(); }); s.listen(0, "127.0.0.1", () => process.send({ port: s.address().port })); } if (process.argv[2]) baslat(process.argv[2]); else console.log(`uclar: /canlilik /hazir /ara /odunc; isinma ${ISINMA} istek; ` + `/ara bagimlilik gerektirmez, /odunc gerektirir`);
uclar: /canlilik /hazir /ara /odunc; isinma 24 istek; /ara bagimlilik gerektirmez, /odunc gerektirir
İki uç arasındaki bütün fark bagimli() çağrısının hangi satırda geçtiğidir. Hazır olma ucunda
geçer, çünkü bağımlılık yoksa süreç iş alamaz. Canlılık ucunda geçmemelidir; birlesik kipi tam
olarak bu hatayı yapar ve tek bir koşul terimi ekler. Süreç iki iş ucu sunar: /ara yerel dizinden
yanıt verir ve üyelik servisine ihtiyaç duymaz, /odunc duyar. Yeniden başlayan süreç yerel dizini
kaybeder ve yirmi dört istek boyunca ikisine de yanıt veremez.
Aynı Senaryo, İki Kip
// saglik/denetleyici.mjs — ayni senaryo iki kipte kosulur: uyelik servisi 15. tikte kapanir, // 40. tikte acilir. Denetleyici her 2 tikte canlilik ve hazir olma ucunu yoklar, 3 ardisik // canlilik hatasinda sureci yeniden baslatir. Her tikte 2 arama ve 2 odunc istegi gonderilir. import { fork } from "node:child_process"; import { writeFileSync } from "node:fs"; import { request } from "node:http"; // SD2 (varsayim): denetim araligi 2 tik, yeniden baslatma esigi 3 ardisik canlilik hatasi. const TIK = 60, KAPALI = [15, 40], DENETIM = 2, ESIK = 3, ISINMA = 24; const cagir = (port, yol) => new Promise((c) => { const r = request({ host: "127.0.0.1", port, path: yol }, (y) => { y.resume(); c(y.statusCode); }); r.on("error", () => c(0)); r.end(); }); const ac = (kip) => new Promise((c) => { const p = fork(new URL("dugum.mjs", import.meta.url), [kip], { stdio: ["ignore", "ignore", "ignore", "ipc"] }); p.once("message", (m) => c([p, m.port])); }); async function kosum(kip) { writeFileSync("bagimlilik.durum", "acik"); let [p, port] = await ac(kip); for (let i = 0; i < ISINMA; i += 1) await cagir(port, "/ara"); // ilk isinma sayilmaz const s = { yeniden: 0, ara: 0, odunc: 0, hazirDegil: 0, toparlanma: -1 }; let ardisik = 0; for (let t = 0; t < TIK; t += 1) { writeFileSync("bagimlilik.durum", t >= KAPALI[0] && t < KAPALI[1] ? "kapali" : "acik"); if (t % DENETIM === 0) { ardisik = (await cagir(port, "/canlilik")) === 200 ? 0 : ardisik + 1; if (ardisik >= ESIK) { ardisik = 0; s.yeniden += 1; p.kill(); await new Promise((c) => p.once("exit", c)); [p, port] = await ac(kip); } if ((await cagir(port, "/hazir")) !== 200) s.hazirDegil += 1; } let iyi = 0; for (const yol of ["/ara", "/ara", "/odunc", "/odunc"]) { if ((await cagir(port, yol)) === 200) iyi += 1; else if (yol === "/ara") s.ara += 1; else s.odunc += 1; } if (t >= KAPALI[1] && iyi === 4 && s.toparlanma < 0) s.toparlanma = t - KAPALI[1]; } p.kill(); return s; } const cikti = []; for (const kip of ["ayri", "birlesik"]) cikti.push([kip, await kosum(kip)]); console.log(`${TIK} tik, tik basina 4 istek; uyelik servisi ${KAPALI[0]}.-${KAPALI[1]}. tikler ` + `arasinda kapali (${KAPALI[1] - KAPALI[0]} tik), isinma ${ISINMA} istek`); console.log(`\n${"kip".padEnd(10)}${"yeniden baslatma".padStart(18)}${"dusen /ara".padStart(12)}` + `${"dusen /odunc".padStart(14)}${"hazir degil".padStart(13)}${"toparlanma".padStart(12)}`); for (const [kip, s] of cikti) console.log(`${kip.padEnd(10)}${String(s.yeniden).padStart(18)}${String(s.ara).padStart(12)}` + `${String(s.odunc).padStart(14)}${(s.hazirDegil + " yoklama").padStart(13)}` + `${(s.toparlanma + " tik").padStart(12)}`); const [a, b] = cikti.map(([, s]) => s); console.log(`\nbirlesik kipte dusen ${b.ara} arama istegi uyelik servisine hic ihtiyac duymuyordu; ` + `ayri kipte bu sayi ${a.ara}`); console.log(`toplam dusen istek: ayri ${a.ara + a.odunc}, birlesik ${b.ara + b.odunc} ` + `(${b.ara + b.odunc - a.ara - a.odunc} istek yalnizca yanlis siniflandirmadan)`);
60 tik, tik basina 4 istek; uyelik servisi 15.-40. tikler arasinda kapali (25 tik), isinma 24 istek kip yeniden baslatma dusen /ara dusen /odunc hazir degil toparlanma ayri 0 0 50 12 yoklama 0 tik birlesik 4 48 58 14 yoklama 4 tik birlesik kipte dusen 48 arama istegi uyelik servisine hic ihtiyac duymuyordu; ayri kipte bu sayi 0 toplam dusen istek: ayri 50, birlesik 106 (56 istek yalnizca yanlis siniflandirmadan)
Yanlış Sınıflandırmanın Bedeli
ayri kipinde yeniden başlatma yok. Üyelik servisi yirmi beş tik boyunca kapalıyken hazır olma ucu
on iki yoklamada olumsuz yanıt verdi — yani trafik yönlendiricisi bu düğümü listeden çıkarabilirdi —
ama süreç yaşadı. Düşen 50 istek yalnız /odunc isteğidir: bunlar zaten üyelik servisi olmadan
yapılamayacak işlerdi. Arama isteklerinin hiçbiri düşmedi. Bağımlılık 40. tikte döndüğünde
toparlanma aynı tikte oldu, çünkü süreç bellekteki dizinini hiç kaybetmemişti.
birlesik kipinde canlılık ucu bağımlılığa bakınca zincir kuruldu. Üç ardışık olumsuz yoklamadan
sonra denetleyici süreci öldürdü, yeni süreç aynı bağımlılığı bulamadığı için o da öldürüldü:
kapalı geçen yirmi beş tikte dört yeniden başlatma. Her yeniden başlatma yerel dizini sıfırladı
ve yirmi dört istek boyunca /ara da yanıt veremedi. Sonuç: 48 arama isteği düştü, oysa bu
isteklerin üyelik servisine hiç ihtiyacı yoktu. /odunc düşüşü de 50’den 58’e çıktı, çünkü
bağımlılık döndükten sonra süreç hâlâ ısınıyordu.
İki sayı bu dersin ölçüsüdür. Toplam düşen istek 50’den 106’ya çıktı; 56 istek yalnızca yanlış sınıflandırma yüzünden düştü. Ve toparlanma süresi 0 tikten 4 tike çıktı: bağımlılık geri geldikten sonra bile sistem, son yeniden başlatmanın ısınma borcunu ödeyene kadar yanıt veremedi. Bu ikinci sayı döngünün asıl niteliğini gösteriyor — yeniden başlatma arızayı uzatır, kısaltmaz.
Ayrımı Kodda Korumak
İki ucun tek bir yardımcı işleve indirgenmesi en sık yapılan hatadır: saglikDurumu() adında bir
işlev yazılır, bütün denetimleri koşar ve iki uç da onu çağırır. Bu yazımda ayrım bir gün sonra
kaybolur, çünkü yeni bir bağımlılık denetimi eklemek isteyen kişi tek bir yere ekler ve o yer her
iki uca birden bağlıdır. Yukarıdaki düğümde iki uç ayrı satırlardır ve aralarında paylaşılan tek
şey bagimli() okuyucusudur; hangi ucun onu çağıracağı her seferinde açıkça yazılır.
Karar için üç maddelik bir ölçüt yeter. Sürecin kendi durumu — kilit, bellek, olay döngüsü — canlılığa girer, çünkü yeniden başlatmak onları düzeltir. Bağımlılıklar ve ısınma hazır olmaya girer, çünkü beklemek düzeltir, öldürmek düzeltmez. Tükenen kaynaklar — disk, bağlantı sayısı — hiçbirine girmez; onlar önceki dersin uyarı kuralına aittir, çünkü ne yeniden başlatma ne trafik kesme onları çözer.
Özet
- Canlılık “bu süreç ölmüş mü” sorusunu yanıtlar ve yeniden başlatmayı tetikler; hazır olma “şu an iş alabilir mi” sorusunu yanıtlar ve yalnız trafiği keser.
- Bir denetim canlılık ucuna ancak başarısızlığında yeniden başlatmak çözümse girer; bağımlılık denetimi bu ölçüte uymaz.
- Aynı yirmi beş tiklik bağımlılık kesintisinde ayrı tutulan uçlar sıfır yeniden başlatma üretti, birleşik uç dört yeniden başlatma üretti.
- Yanlış sınıflandırma, bağımlılığa hiç ihtiyacı olmayan 48 arama isteğini düşürdü; toplam düşen istek 50’den 106’ya çıktı ve 56 istek yalnızca bu hatadan kaynaklandı.
- Yeniden başlatma arızayı uzattı: bağımlılık döndükten sonra toparlanma 0 tik yerine 4 tik sürdü, çünkü her yeniden başlatma yerel dizini sıfırladı.
- Ayrım kodda tek bir koşul terimidir ve iki ucu ortak bir
saglikDurumu()işlevine indirgemek onu ilk eklemede kaybettirir.
Sonraki Adım
Bu konu telemetriyi karara çevirdi: gösterge tanımlandı, dört sinyal hesaplandı, hedefin tersi harcanabilir bir bütçeye dönüştü, uyarı kuralı belirtiden yazıldı ve iki sağlık ucu ayrıldı. Artık bir sınırın aşıldığı söylenebiliyor. Söylenmesiyle birlikte değişen bir şey ise yok. Üyelik servisi kapalıyken ödünç düğümü aynı isteği aynı biçimde göndermeye, aynı süre boyunca beklemeye ve aynı hatayı almaya devam etti; hazır olma ucu bunu bildirdi ama isteği durduran, kısaltan ya da başka bir yola çeviren hiçbir kod yoktu. Ölçmek gözlemi değiştirir, davranışı değiştirmez. Sonraki konu davranışı değiştiren kalıpları kodun içine yerleştirir ve her birinin hangi katmanda durduğunu, kaç çağrı yerini sardığını ve hangi çağrı yerini atladığını sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.