İçeriğe geç
academia.sh

Ders 19 / 20

Süreç Yönetimi ve Sağlamlık

Sinyallerin karşılanması, zarif kapanmanın aşamaları, canlılık ile hazırlık uçlarının ayrımı, yeniden başlatma sözleşmesi ve kapanma süresinin sınırlanması.

İçindekiler

Buraya kadar süreç hep elle başlatıldı ve uçbirim kapatılarak durduruldu. Üretimde bu iş bir denetçiye aittir: süreci başlatır, çöktüğünde yeniden açar, güncelleme sırasında durdurur.

Denetçi ile süreç arasında bir sözleşme vardır ve bu dersin konusu sözleşmenin süreç tarafıdır. Üç madde: kapanma isteğini karşılamak, süren işi bitirip yeni iş almamak, kendi durumunu bildirmek.

Kapanma İsteği Bir Sinyaldir

Kabuk Programlama kursunda sinyaller ve tuzaklar ele alınmıştı; aynı düzenek burada süreç nesnesinin olayları olarak görünür.

Denetçinin kapanma isteği SIGTERM sinyalidir. Uçbirimde kesme tuşuna basılması SIGINT üretir. İkisinin de varsayılan davranışı süreci hemen sonlandırmaktır; bir dinleyici takıldığında varsayılan davranış devre dışı kalır ve karar programa geçer.

Yakalanamayan bir sinyal de vardır. Denetçi, kibar istekten sonra tanıdığı süre içinde süreç kapanmazsa onu kullanır ve süreç hiçbir temizlik yapamadan sonlanır. Bu yüzden kapanışın kendi süre sınırı olmalıdır: denetçinin sınırından önce bitirmeyen bir kapanış, hiç yapılmamış sayılır.

Zarif Kapanmanın Aşamaları

Zarif kapanma (graceful shutdown), sürecin yeni iş almayı bırakıp elindeki işi bitirerek sonlanmasıdır. Dört aşaması vardır ve sıraları önemlidir.

// saglam-sunucu.mjs
import { createServer } from 'node:http';

const CIKIS_BEKLEME_MS = 500;    // denetciye "artik hazir degilim" demek icin
const KAPANMA_SINIRI_MS = 3000;  // suren istekler icin verilen en cok sure

let hazir = false;
let kapaniyor = false;

const sunucu = createServer((istek, yanit) => {
  if (istek.url === '/canli') {                 // surec ayakta mi
    yanit.writeHead(200).end('canli\n');
    return;
  }
  if (istek.url === '/hazir') {                 // istek almaya hazir mi
    const kod = hazir && !kapaniyor ? 200 : 503;
    yanit.writeHead(kod).end(kod === 200 ? 'hazir\n' : 'hazir degil\n');
    return;
  }
  if (istek.url === '/yavas') {                 // suren bir istegi taklit eder
    setTimeout(() => yanit.writeHead(200).end('bitti\n'), 800);
    return;
  }
  yanit.writeHead(404).end('bulunamadi\n');
});

sunucu.listen(8791, '127.0.0.1', () => {
  hazir = true;
  console.log(`dinleniyor, surec kimligi ${process.pid}`);
});

function kapan(sinyal) {
  if (kapaniyor) return;
  kapaniyor = true;
  console.log(`${sinyal} alindi; hazirlik ucu artik 503 donuyor`);

  setTimeout(() => {
    console.log('yeni baglanti kabul edilmiyor');

    const zorlayici = setTimeout(() => {
      console.log('sure doldu; kalan baglantilar kesiliyor');
      sunucu.closeAllConnections();
    }, KAPANMA_SINIRI_MS);
    zorlayici.unref();                           // tek basina sureci ayakta tutmasin

    sunucu.close(() => {
      clearTimeout(zorlayici);
      console.log('tum istekler tamamlandi; cikiliyor');
    });
    sunucu.closeIdleConnections();
  }, CIKIS_BEKLEME_MS).unref();
}

process.on('SIGTERM', () => kapan('SIGTERM'));
process.on('SIGINT', () => kapan('SIGINT'));

Denemeyi bir betikle yürütelim:

#!/bin/sh
# kapanma-denemesi.sh
node saglam-sunucu.mjs &
sunucuPid=$!
sleep 1

printf 'kapanmadan once /hazir  -> '; curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8791/hazir

# Suren bir istek baslat, yanit beklerken sinyali gonder
curl -s http://127.0.0.1:8791/yavas > yavas-yanit.txt &
istekPid=$!
sleep 0.1
kill -TERM "$sunucuPid"

sleep 0.2
printf 'kapanma sirasinda /hazir -> '; curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8791/hazir

wait "$istekPid"
echo "suren istegin yaniti: $(cat yavas-yanit.txt)"
wait "$sunucuPid"
echo "surecin cikis kodu: $?"
sh kapanma-denemesi.sh
dinleniyor, surec kimligi 18312
kapanmadan once /hazir  -> 200
SIGTERM alindi; hazirlik ucu artik 503 donuyor
kapanma sirasinda /hazir -> 503
yeni baglanti kabul edilmiyor
tum istekler tamamlandi; cikiliyor
suren istegin yaniti: bitti
surecin cikis kodu: 0

Süreç kimliği her çalıştırmada değişir. Çıktının anlattığı sıra şudur.

Birinci aşama: hazır olmadığını bildir. Sinyal alınır alınmaz hazırlık ucu 503 dönmeye başlar. Sürecin önünde bir yük dağıtıcı varsa, onu listeden çıkarması için bir pencere gerekir; bu pencere olmadan dağıtıcı, kapanmakta olan sürece istek göndermeye devam eder ve o istekler düşer.

İkinci aşama: yeni bağlantı alma. Bekleme süresi dolduğunda close çağrılır. Bu çağrı dinleyen soketi kapatır ama var olan bağlantıları koparmaz. Boşta bekleyen kalıcı bağlantılar ayrıca kapatılır; yoksa istemci hiçbir istek göndermese de bağlantıyı açık tutar ve kapanış uzar.

Üçüncü aşama: süren işi bitir. close geri çağrısı, son istek de tamamlandığında çalışır. Yukarıdaki çıktıda süren isteğin bitti yanıtını aldığı görülüyor — sinyal, o istek işlenirken gönderilmişti.

Dördüncü aşama: süre sınırı. Bir istemci bağlantıyı hiç bırakmazsa kapanış sonsuza kadar sürer. Zorlayıcı zamanlayıcı bu durumu keser. unref çağrısı önemlidir: bu zamanlayıcı tek başına sürecin ayakta kalma nedeni olmamalıdır, yoksa her şey bitse bile süreç sınır dolana kadar bekler.

Çıkış kodu sıfırdır. Denetçi için bu, “istenerek kapandı” demektir ve yeniden başlatma politikasına göre farklı davranmayı sağlar.

Kapanışta yapılacak başka işler de vardır: bekleyen kütük satırlarını aktarmak, açık dosyaları kapatmak, elde kalan özeti diske yazmak. Bunların hepsi close geri çağrısının içinde, süreç sonlanmadan yapılır — Süreç Nesnesi dersinde görüldüğü gibi exit kancasında eşzamansız iş yapılamaz.

Canlılık ve Hazırlık

İki uç iki ayrı soruya yanıt verir ve karıştırılmaları hizmeti kararsızlaştırır.

Canlılık (liveness) sorusu: süreç yaşıyor mu, yoksa öldürülüp yeniden mi başlatılmalı? Yanıtı yalnızca sürecin kendi durumuna bakar; bağımlılıklarını denetlemez.

Hazırlık (readiness) sorusu: bu süreç şu anda istek alabilir mi? Yanıtı açılış tamamlandı mı, kapanış başladı mı gibi geçici durumlara bakar.

Ayrımın gerekçesi şudur: bir bağımlılık geçici olarak erişilemez olduğunda süreç hazır değildir ama canlıdır. Canlılık ucu bağımlılığı denetlerse, bağımlılığın kısa bir kesintisi bütün süreçlerin yeniden başlatılmasına yol açar — hem kesintiyi uzatan hem de kendi başına arıza üreten bir davranış.

Uçlar hafif olmalıdır: her denetim çağrısı bir veritabanı sorgusu yapıyorsa, denetimin kendisi yüke dönüşür.

Yeniden Başlatma Sözleşmesi

Süreç öldüğünde yeniden başlatmak sürecin işi değildir; kendi kendini yeniden başlatamaz. Bu iş, işletim sisteminin hizmet denetçisine veya Küme ve İşçi İş Parçacıkları dersindeki birincil sürece aittir.

Sürece düşen üç şey vardır.

Anlamlı çıkış kodu vermek. Sıfır istenen kapanış, sıfırdan farklı bir değer arıza demektir. Yapılandırma Yönetimi dersinde yapılandırma hatası için ayrı bir kod seçilmesinin nedeni budur: denetçi, düzelmeyeceği belli olan bir hatayı sonsuza kadar yeniden denemek yerine durabilir.

Hızlı ve yinelemeye dayanıklı açılmak. Yeniden başlatma bir kurtarma yöntemiyse, açılışın kendisi güvenilir olmalıdır. Açılışta yapılan iş, yarım kalmış bir önceki çalışmanın izlerini bulduğunda hata vermemelidir — Dosya Sistemi dersindeki geçici ada yazıp yerine koyma kalıbı bu güvenceyi sağlar.

Yeniden başlatma fırtınasını hesaba katmak. Hemen çöken bir süreci hemen yeniden açmak, saniyede yüzlerce açılış üretir ve makineyi meşgul eder. Denetçiler bunu artan gecikmeyle önler; sürecin bu gecikmeyi kısaltacak biçimde davranmaması, örneğin sağlıksız durumda sahte bir başarı bildirmemesi gerekir.

Sürecin kendi kendine yeniden başlatma denemesi ise ayrı bir yanlıştır: bozulmuş bir durumdan çıkmanın yolu, o durumu taşıyan belleği bırakmaktır. Yeniden başlatmanın işe yaramasının nedeni de budur — Hata Yönetimi Stratejisi dersinde son çare kancasının “kaydet ve çık” ile sınırlanmasının gerekçesi.

Özet

  • Kapanma isteği bir sinyaldir; dinleyici takıldığında varsayılan sonlanma davranışı yerini programın kararına bırakır.
  • Zarif kapanma dört aşamalıdır: hazır olmadığını bildir, yeni bağlantı alma, süren işi bitir, süre sınırında kalanları kes.
  • Zorlayıcı zamanlayıcı unref ile işaretlenir; aksi hâlde her şey bittiğinde bile süreç sınır dolana kadar ayakta kalır.
  • Canlılık ucu yalnızca sürecin kendi durumuna, hazırlık ucu geçici duruma bakar; canlılık ucunun bağımlılık denetlemesi kesintileri yayar.
  • Yeniden başlatma denetçinin işidir; sürece düşen anlamlı çıkış kodu vermek, hızlı ve yinelemeye dayanıklı açılmak ve fırtınayı beslememektir.

Sonraki Adım

Ölçüm toplayıcı çalışır durumda: yapılandırılır, hataları sınıflandırır, kütüğünü yapılandırılmış yazar, sınanır, izlenir ve düzgün kapanır. Geriye tek bir soru kalıyor — bu kodun yeniden kullanılabilir bölümü başka projelere nasıl taşınır? Son ders paketleme sözleşmesini ele alır: giriş noktalarının bildirilmesi, iç dosyaların gizlenmesi ve sürüm yayımının kuralları.

İ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