Ders 10 / 16
Süreç Yöneticileri
Uygulamanın altındaki katmanın iki ayarı: çöken sürecin anında, geri çekilmeli ve sınırlı denemeyle yeniden başlatılmasının düşen istek sayısına etkisi, hiç başlamayan süreçte başlatma döngüsünün politikaya göre kaç kez döndüğü, bellek sınırının aşılmasında sürecin öldürülmesi ile kayıt atıp ayakta kalması arasındaki fark ve durdurma işaretinden sonra tanınan bekleme süresinin boşaltılan istek sayısını nasıl belirlediği.
İçindekiler
Önceki ders uygulamanın önündeki katmanı yapılandırdı ve sekiz koşumun hepsinde tek bir şeyi varsaydı: uygulama süreci ayaktaydı. Elle başlatıldı, işi bitince elle durduruldu, arada hiç çökmedi.
Üretimde süreci başlatan, çöktüğünde yeniden başlatan, ne kadar bellek kullanabileceğine karar veren ve durdurulacağı zaman ona haber veren başka bir bileşen vardır: süreç yöneticisi. Vekilin ayarları isteğin içeriğini değiştiriyordu; buranın ayarları isteğin yanıtlanıp yanıtlanmayacağını değiştirir.
İki Ayar, Bir İşçi
Süreç yöneticisinin ölçülecek iki ayarı var. Birincisi yeniden başlatma politikası: süreç çıktığında ne kadar beklenip yeniden başlatılacağı ve kaç deneme sonra vazgeçileceği. İkincisi kaynak sınırı: sürecin ne kadar bellek tutabileceği ve sınır aşıldığında ne yapılacağı. Üçüncü bir ayar ikisinin dışında durur ve ikisinden de sessizdir: durdurma işaretinden sonra sürece tanınan bekleme süresi.
- DP1. İşçi her açık ödünç kaydı için 2 MB ayırır ve tuttuğu toplamı yöneticiye bildirir; sınır bu bildirim üzerinden uygulanır. Gerçek dağıtımda aynı değer işletim sisteminden okunur.
- DP2. Bir isteğin işi sabit sürelidir. Bellek ölçümünde yanıtlar kabul sırasına göre 100 milisaniye arayla verilir; sınırın hangi anda aşıldığı ve o anda kaç isteğin uçuşta olduğu böylece belirli olur.
- DP3. Bu derste tek bir işçi süreci vardır. Kopya sayısı sonraki dersin konusudur.
İşçi, ödünç uygulamasının çalışan hâlidir ve dört davranışı bir mod dizgesinden alır.
// topoloji/isci.mjs — odunc isci sureci. Mod dizgesi davranisini belirler: // cok=<n> n istek yanitladiktan sonra cikar | baslarken-cok=1 dinlemeden once cikar // is=<ms> bir istegin suresi basamak=<ms> kabul sirasina gore isin uzamasi (yanitlar ayrisir) // bellek=yumusak sinirda en eski kayitlarin yarisini atar ve ayakta kalir // kapanis=zarif|ani durdurma isaretinde bekleyen istekleri bosaltir ya da hemen cikar import { createServer } from "node:http"; const [nokta, modMetni] = [Number(process.argv[2]), process.argv[3] ?? ""]; const M = Object.fromEntries(modMetni.split(",").filter(Boolean).map((p) => p.split("="))); const KAYIT_MB = 2; // tutulan odunc kaydi basina bellek const SINIR_MB = 40; // yumusak sinir esigi const tutulan = []; let yanit = 0, atilan = 0, kabul = 0, kapaniyor = false; if (Number.isInteger(nokta) === false) { console.log("kullanim: node topoloji/isci.mjs <nokta> <mod>"); process.exit(0); } if (M["baslarken-cok"] === "1") process.exit(9); const sunucu = createServer((istek, cevap) => { cevap.sendDate = false; if (kapaniyor) { cevap.writeHead(503).end("kapaniyor"); return; } const sira = kabul++; tutulan.push(new Array((KAYIT_MB * 1024 * 1024) / 8).fill(1)); // acik odunc kaydi bellekte durur if (M.bellek === "yumusak" && tutulan.length * KAYIT_MB > SINIR_MB) { atilan += tutulan.splice(0, tutulan.length >> 1).length; // en eski yarisi atilir, surec yasar } setTimeout(() => { yanit += 1; cevap.writeHead(200, { "x-tutulan-mb": String(tutulan.length * KAYIT_MB) }).end("odunc"); if (process.send !== undefined) process.send({ tutulanMB: tutulan.length * KAYIT_MB, yanit, atilan }); if (M.cok !== undefined && yanit >= Number(M.cok)) process.exit(9); }, Number(M.is ?? 40) + sira * Number(M.basamak ?? 0)); }); process.on("SIGTERM", () => { if (M.kapanis === "zarif") { kapaniyor = true; sunucu.close(() => process.exit(0)); } else process.exit(0); // bekleyen istekler yaniti gormeden dusuyor }); sunucu.listen(nokta, "127.0.0.1");
Yönetici işçiyi başlatır, bildirimlerini dinler, çıkışını yakalar ve politikayı uygular.
// topoloji/yonetici.mjs — surec yoneticisi. Ayar dizgesi: // politika=aninda|geri-cekilmeli|sinirli:<n> bellek=yok|sert:<mb> // sure=<ms> 0 ise durdurma isaretini bekler ve ozet basar; >0 ise o anda zarif durdurmayi baslatir // bekleme=<ms> durdurma isaretinden sonra sert oldurmeye kadar taninan sure import { fork } from "node:child_process"; const [nokta, ayarMetni, isciMod, etiket] = [process.argv[2], process.argv[3] ?? "", process.argv[4] ?? "", process.argv[5] ?? "-"]; const A = Object.fromEntries(ayarMetni.split(",").filter(Boolean).map((p) => p.split("="))); const GERI = [100, 200, 400, 800, 1600]; // geri cekilme merdiveni (ms) let deneme = 0, gecikme = 0, oldurme = 0, tepe = 0, atilan = 0, vazgecti = false, duruyor = false, cocuk = null; function baslat() { let olduruldu = false; cocuk = fork("topoloji/isci.mjs", [nokta, isciMod], { stdio: ["ignore", "ignore", "ignore", "ipc"] }); cocuk.on("message", (m) => { tepe = Math.max(tepe, m.tutulanMB); atilan = m.atilan; const sert = (A.bellek ?? "yok").startsWith("sert:") ? Number(A.bellek.split(":")[1]) : Infinity; if (m.tutulanMB > sert && olduruldu === false) { // sinir asildi: surec olduruluyor olduruldu = true; oldurme += 1; cocuk.kill("SIGKILL"); } }); cocuk.on("exit", () => { if (duruyor === false) yeniden(); }); } function yeniden() { const en = (A.politika ?? "").startsWith("sinirli:") ? Number(A.politika.split(":")[1]) : Infinity; if (deneme >= en) { vazgecti = true; return; } const g = A.politika === "geri-cekilmeli" ? GERI[Math.min(deneme, GERI.length - 1)] : 0; deneme += 1; gecikme += g; setTimeout(baslat, g); } if (nokta === undefined) console.log("kullanim: node topoloji/yonetici.mjs <nokta> <ayar> <isci-mod> <etiket>"); else { setInterval(() => {}, 60000); // vazgecildikten sonra da ozet icin ayakta kalinir baslat(); process.on("SIGTERM", () => { duruyor = true; cocuk?.kill("SIGKILL"); console.log(`${etiket} yonetici: ${deneme} yeniden baslatma, ${gecikme} ms gecikme, ${oldurme} oldurme, ` + `tepe ${tepe} MB, ${atilan} kayit atildi${vazgecti ? ", vazgecti" : ""}`); process.exit(0); }); if (Number(A.sure ?? 0) > 0) setTimeout(() => { duruyor = true; cocuk.kill("SIGTERM"); // once zarif kapanma istegi const sert = setTimeout(() => cocuk.kill("SIGKILL"), Number(A.bekleme ?? 500)); cocuk.on("exit", () => { clearTimeout(sert); process.exit(0); }); // bekleme dolmadan cikarsa temiz }, Number(A.sure)); }
Yük üreteci istekleri sabit aralıkla ya da hepsini birden gönderir ve üç sonucu ayırır: yanıtlanan, temiz reddedilen, düşen.
// topoloji/yuk.mjs — yuk ureteci. Kip "aralikli": adet istek, aralik ms arayla. // Kip "esz": adet istek ayni anda. Baglanti kurulamayan ya da yanitsiz kapanan istek "dusen" sayilir. import { request } from "node:http"; const [nokta, kip, adet, aralik, etiket] = [Number(process.argv[2]), process.argv[3], Number(process.argv[4]), Number(process.argv[5]), process.argv[6] ?? "-"]; const bekle = (ms) => new Promise((c) => setTimeout(c, ms)); let yanitlanan = 0, reddedilen = 0, dusen = 0; const bir = () => new Promise((coz) => { const r = request({ port: nokta, path: "/odunc", agent: false }, (y) => { y.resume(); y.on("end", () => { if (y.statusCode === 200) yanitlanan += 1; else reddedilen += 1; coz(); }); y.on("aborted", () => { dusen += 1; coz(); }); }); r.on("error", () => { dusen += 1; coz(); }); r.end(); }); if (Number.isInteger(nokta) === false) console.log("kullanim: node topoloji/yuk.mjs <nokta> <kip> <adet> <aralik> <etiket>"); else { if (kip === "esz") await Promise.all(Array.from({ length: adet }, bir)); else { const acik = []; for (let i = 0; i < adet; i += 1) { acik.push(bir()); await bekle(aralik); } await Promise.all(acik); } console.log(`${etiket} yuk: ${adet} istek, ${yanitlanan} yanit, ${reddedilen} reddedilen, ${dusen} dusen`); }
Çöken Süreç ve Başlamayan Süreç
İlk düzenek aynı çökmeyi üç politikayla karşılıyor. İşçi her dört yanıttan sonra çıkıyor, yük 24 isteği 100 milisaniye arayla gönderiyor. Ardından aynı üç politika hiç başlamayan bir işçiyle koşuyor: süreç dinlemeye geçmeden çıkıyor.
# olc1.sh — ayni cokme, uc yeniden baslatma politikasi. Isci her 4 yanittan sonra cikiyor. kos() { # kos <politika> <isci-mod> <adet> <etiket> node topoloji/yonetici.mjs 8941 "politika=$1,sure=0" "$2" "$4" & M=$! sleep 0.5 node topoloji/yuk.mjs 8941 aralikli "$3" 100 "$4" kill -TERM $M; wait $M; sleep 0.2 } kos aninda "cok=4,is=40" 24 "cokme / aninda " kos geri-cekilmeli "cok=4,is=40" 24 "cokme / geri cekilmeli" kos sinirli:2 "cok=4,is=40" 24 "cokme / sinirli (2) " kos aninda "baslarken-cok=1" 8 "baslamiyor / aninda " kos geri-cekilmeli "baslarken-cok=1" 8 "baslamiyor / geri cek." kos sinirli:2 "baslarken-cok=1" 8 "baslamiyor / sinirli 2"
cokme / aninda yuk: 24 istek, 20 yanit, 0 reddedilen, 4 dusen cokme / aninda yonetici: 5 yeniden baslatma, 0 ms gecikme, 0 oldurme, tepe 8 MB, 0 kayit atildi cokme / geri cekilmeli yuk: 24 istek, 14 yanit, 0 reddedilen, 10 dusen cokme / geri cekilmeli yonetici: 3 yeniden baslatma, 700 ms gecikme, 0 oldurme, tepe 8 MB, 0 kayit atildi cokme / sinirli (2) yuk: 24 istek, 12 yanit, 0 reddedilen, 12 dusen cokme / sinirli (2) yonetici: 2 yeniden baslatma, 0 ms gecikme, 0 oldurme, tepe 8 MB, 0 kayit atildi, vazgecti baslamiyor / aninda yuk: 8 istek, 0 yanit, 0 reddedilen, 8 dusen baslamiyor / aninda yonetici: 43 yeniden baslatma, 0 ms gecikme, 0 oldurme, tepe 0 MB, 0 kayit atildi baslamiyor / geri cek. yuk: 8 istek, 0 yanit, 0 reddedilen, 8 dusen baslamiyor / geri cek. yonetici: 4 yeniden baslatma, 1500 ms gecikme, 0 oldurme, tepe 0 MB, 0 kayit atildi baslamiyor / sinirli 2 yuk: 8 istek, 0 yanit, 0 reddedilen, 8 dusen baslamiyor / sinirli 2 yonetici: 2 yeniden baslatma, 0 ms gecikme, 0 oldurme, tepe 0 MB, 0 kayit atildi, vazgecti
İlk üç ikili aynı çökmeyi karşılıyor ve sıralama beklenenin tersine dönüyor. Anında politikası 24 isteğin 20’sini yanıtladı, dördü düştü (bu koşumda). Geri çekilmeli politika 14 yanıt verdi, on istek düştü — merdiven 100, 200 ve 400 milisaniyeyle toplam 700 milisaniyelik bir bekleme ekledi ve o bekleme boyunca gelen her istek düştü. Sınırlı politika iki denemeden sonra vazgeçti ve kalan on iki isteğin hepsi düştü; bu son sayı politikanın tanımından çıkar, koşumdan değil.
Yanıtlanan ve düşen sayılar sabit aralıklı bir yükten okunur ve makinenin o andaki yüküne bağlıdır; koşumdan bağımsız olan, politikaların eklediği bekleme ile deneme sayılarıdır.
Çökme geçici olduğunda sıralama nettir: geri çekilme, bir sonraki denemenin başarılı olacağı durumda saf kayıptır. Merdivenin varlık nedeni son üç satırda görünüyor.
İşçi hiç başlamadığında üç politika da aynı sonucu veriyor — sekiz isteğin sekizi düşüyor. Fark istek tarafında değil, makinenin harcadığı işte: anında politikası aynı 0,8 saniyelik pencerede onlarca süreç başlattı (bu koşumda 43), geri çekilmeli politika dört, sınırlı politika iki. Bu üç sayının ikisi koşumdan bağımsızdır: geri çekilme merdiveni 100, 200, 400 ve 800 ile o pencereye dört deneme sığdırır, sınırlı politika tanımı gereği ikide durur. Anında politikasının sayısı ise pencere uzunluğu ile süreç başlatma maliyetinin oranıdır ve üst sınırı yoktur: pencere büyüdükçe büyür, makine hızlandıkça büyür.
Bir ayrım daha var ve en sessiz olanı odur. Sınırlı politika vazgeçti diye yazıyor; öteki ikisi sonsuza kadar denemeye devam eder. Ayakta olmayan bir sistem, hiç durmadan başlatılmaya çalışıldığı sürece “başlatılıyor” görünür.
Sınırı Aşmak: Öldürmek ve Yavaşlatmak
İkinci düzenek bellek sınırını iki yerde uyguluyor. Sert sınırda yönetici bildirimi okur ve eşiği geçen süreci öldürür. Yumuşak sınırda işçi kendi eşiğini görür, en eski kayıtların yarısını atar ve ayakta kalır. Yirmi dört istek birden gönderiliyor, yani sınır aşıldığında hepsi uçuşta. Üçüncü düzenek durdurma işaretini uçuşta altı istek varken gönderiyor.
# olc2.sh — bellek siniri iki bicimde, ardindan durdurma isaretinde ucustaki istekler. bellek() { # bellek <yonetici-ayar> <isci-mod> <etiket> node topoloji/yonetici.mjs 8942 "$1" "$2" "$3" & M=$! sleep 0.5 node topoloji/yuk.mjs 8942 esz 24 0 "$3" kill -TERM $M; wait $M; sleep 0.2 } bellek "politika=aninda,bellek=sert:40,sure=0" "is=200,basamak=100" "sert sinir 40 MB " bellek "politika=aninda,bellek=yok,sure=0" "is=200,basamak=100,bellek=yumusak" "yumusak sinir 40 MB" kapanis() { # kapanis <yonetici-ayar> <isci-mod> <etiket> node topoloji/yonetici.mjs 8943 "$1" "$2" "$3" & M=$! sleep 0.5 node topoloji/yuk.mjs 8943 esz 6 0 "$3" wait $M; sleep 0.2 } kapanis "politika=aninda,sure=700,bekleme=500" "is=300,kapanis=zarif" "zarif, bekleme 500 ms" kapanis "politika=aninda,sure=700,bekleme=50" "is=300,kapanis=zarif" "zarif, bekleme 50 ms" kapanis "politika=aninda,sure=700,bekleme=500" "is=300,kapanis=ani" "ani kapanma "
sert sinir 40 MB yuk: 24 istek, 1 yanit, 0 reddedilen, 23 dusen sert sinir 40 MB yonetici: 1 yeniden baslatma, 0 ms gecikme, 1 oldurme, tepe 48 MB, 0 kayit atildi yumusak sinir 40 MB yuk: 24 istek, 24 yanit, 0 reddedilen, 0 dusen yumusak sinir 40 MB yonetici: 0 yeniden baslatma, 0 ms gecikme, 0 oldurme, tepe 28 MB, 10 kayit atildi zarif, bekleme 500 ms yuk: 6 istek, 6 yanit, 0 reddedilen, 0 dusen zarif, bekleme 50 ms yuk: 6 istek, 0 yanit, 0 reddedilen, 6 dusen ani kapanma yuk: 6 istek, 0 yanit, 0 reddedilen, 6 dusen
Sert sınır 48 MB’de aşılıyor ve süreç öldürülüyor: yirmi dört isteğin biri yanıtlanıyor, yirmi üçü düşüyor, bir yeniden başlatma yapılıyor. Yumuşak sınırda aynı yirmi dört isteğin yirmi dördü de yanıtlanıyor, hiç düşen yok, hiç yeniden başlatma yok; bedel on kaydın atılması ve tepenin 48 yerine 28 MB’de kalmasıdır.
İki satır aynı eşiği aynı yükte karşılıyor ve hangi işin kaybolduğu değişiyor. Öldürmede kaybolan şey uçuştaki isteklerdir: yirmi üç ödünç işlemi yarıda kesilir ve istemci yanıt almaz. Kayıt atmada kaybolan şey bellekteki geçmiştir: süreç yaşar, istek yanıtlanır, atılan kaydın gerektiğinde yeniden okunması gerekir. Birincisi görünür bir arıza üretir, ikincisi bir yavaşlama üretir.
Yirmi üç sayısı sınırın aşıldığı anda uçuşta olan istek sayısıdır: öldürmenin faturası o anda taşınan işle ölçülür.
Yumuşak sınırın sessiz tarafı da buradadır: hiçbir sayaç kırmızıya dönmez. Yirmi dört istek yanıtlandı, düşen yok. Atılan kayıt sayısını raporlamayan bir düzenekte bu satır kusursuz görünür ve bellek eşiğinin sürekli aşıldığı bilgisi hiçbir yere yazılmaz.
Bekleme Süresi
Son üç satır durdurma işaretini uçuşta altı istek varken gönderiyor. İstek başına iş 300 milisaniye.
Zarif kapanmada işçi yeni istek almayı bırakır ve bekleyenleri boşaltır; bekleme 500 milisaniye olduğunda altı isteğin altısı boşaltıldı. Aynı kod, bekleme 50 milisaniyeye çekildiğinde altı isteğin altısını kaybetti: işçi boşaltmaya başladı, yönetici sonunu beklemedi ve süreci öldürdü. Ani kapanmada işçi hiç boşaltmadı ve sonuç aynı oldu.
Üç satırın ikisi aynı sonucu veriyor, ama nedenleri farklı: birinde boşaltma yok, ötekinde boşaltma var ama tanınan süre yetmiyor. Kod tarafında ikincisi doğru yazılmıştır; ayar tarafında ikisi de aynı kadar kayıptır. Bekleme süresi, uygulamanın en uzun isteğinden büyük olmadıkça zarif kapanma kodu çalışmaz — ve kodun çalışmadığı hiçbir yere yazılmaz.
Ayarın Yeri
| Ayar | Yeri | Kaç yerde | Yanlış değerin sessiz sonucu |
|---|---|---|---|
| Yeniden başlatma politikası | süreç yöneticisi | 1 (her düğüm) | geçici çökmede fazladan düşen istek, kalıcı çökmede sonsuz başlatma döngüsü |
| Deneme sınırı | süreç yöneticisi | 1 | sınır yoksa arıza “başlatılıyor” olarak görünür |
| Bellek sınırı ve biçimi | yönetici (sert) ya da uygulama kodu (yumuşak) | 2 | öldürmede uçuştaki bütün istekler, kayıt atmada sessiz yavaşlama |
| Bekleme süresi | süreç yöneticisi | 1 | zarif kapanma kodu yazılıdır ve hiç çalışmaz |
Dört satırın üçü tek bir yerde durur ve tek bir sayıdır. Etkileri ancak bir çökme ya da bir yayın sırasında görünür.
Özet
- Aynı geçici çökmede anında yeniden başlatma 24 isteğin 20’sini yanıtladı, geri çekilmeli politika 14’ünü, iki denemeyle sınırlı politika 12’sini (ilk ikisi bu koşumda); geri çekilmenin eklediği 700 milisaniyelik bekleme doğrudan düşen isteğe dönüştü.
- Süreç hiç başlamadığında üç politika da sekiz isteğin sekizini kaybetti; fark harcanan işte oldu — anında politikası aynı pencerede onlarca süreç başlattı (bu koşumda 43), geri çekilmeli dört, sınırlı iki, ve yalnız sonuncusu vazgeçtiğini bildirdi.
- Geri çekilme merdiveninin ve deneme sınırının ürettiği sayılar koşumdan bağımsızdır; anında politikasının başlatma sayısının üst sınırı yoktur.
- Bellek sınırı sert uygulandığında yirmi dört isteğin yirmi üçü düştü ve bir yeniden başlatma yapıldı; yumuşak uygulandığında hiç istek düşmedi ve bedel on kaydın atılması oldu. Düşen istek sayısı, sınırın aşıldığı anda uçuşta olan iş kadardır.
- Zarif kapanmada 500 milisaniyelik bekleme uçuştaki altı isteğin altısını boşalttı; aynı kod 50 milisaniyelik beklemeyle altısını da kaybetti ve sonuç ani kapanmayla aynı çıktı.
Sonraki Adım
Bu iki ders tek bir süreçle çalıştı. Vekil tek bir hedefe iletti, yönetici tek bir işçiyi başlattı ve bellek sınırı o tek sürecin sınırıydı. Yükü karşılamanın iki yolu vardır ve ikisi de bu tek sayıya dokunur: sürece daha çok kaynak vermek ya da daha çok süreç açmak. İki yol aynı isteği karşılar, aynı yanıtı döndürür ve arka planda birbirinden çok farklı bir fatura üretir. Sonraki ders aynı yükü tek büyük süreçle ve dört küçük süreçle karşılar; bellek toplamını, ayağa kalkma süresini ve her sürecin kendi bağlantı havuzunu açmasından doğan çarpanı ölçer — bir sürecin havuz ayarı doğruyken dört sürecin aynı ayarı depoya dört katı bağlantı gönderir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.