İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat