---
title: 'Uygulama Çalışma Zamanı'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/uygulama-calisma-zamani'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:52+00:00'
license: 'CC BY-SA 4.0'
---

# Uygulama Çalışma Zamanı

Uygulamanın altındaki süreç modeli: olay döngüsünü bloklayan işin diğer istekleri ne kadar beklettiği ölçülür, iş parçacığı havuzunun sınırı gösterilir ve ölçekleme biriminin neden süreç olduğu aynı bağlantı noktasını paylaşan işçilerle sayılır.

Önceki ders vekilin arkasındaki uygulama sunucusunu tek bir süreç olarak çalıştırdı ve
sorusunu açık bıraktı: bir istek uzun sürdüğünde diğerlerine ne olur, aynı uygulamanın iki
kopyası aynı bağlantı noktasını nasıl paylaşır, ölçekleme kararının birimi süreç müdür yoksa
iş parçacığı mıdır? Bu ders o üç soruyu ölçerek yanıtlar.

Ölçüm için kütüphane ödünç servisinin iki ucu kullanılacak: üye parolasını doğrulayan pahalı
bir uç ve katalogdan tek satır döndüren ucuz bir uç. İkisi aynı süreçte yaşadığında pahalı
işin ucuz isteklere ne yaptığı, aradaki gecikmeyle görünür hâle gelir.

## Çalışma Zamanı Neyi Sağlar

**Çalışma zamanı** (runtime), uygulama kodunun üstünde durduğu katmandır: kodu yürüten
motoru, işletim sisteminin soket ve dosya arayüzlerine açılan kapıları ve bu ikisinin
arasında işleri sıraya koyan **olay döngüsünü** (event loop) barındırır. Node.js Çalışma
Zamanı kursunda kurulan model burada olduğu gibi geçerlidir; bu derste yeni olan, aynı
modelin bir sunucu uygulamasının ölçekleme kararlarını nasıl belirlediğidir.

Model tek cümleyle şudur: uygulama kodu **tek bir iş parçacığında** (thread) çalışır. Bir
istek işleyicisi çalışırken başka hiçbir işleyici çalışmaz. Olay döngüsü, bir işleyici
bittikten sonra kuyruktaki sıradaki işi alır. Girdi-çıktı beklemeleri — disk okuma, ağ
çağrısı, veritabanı sorgusu — bu kuyruğu meşgul etmez; çalışma zamanı beklemeyi işletim
sistemine devreder ve sonuç geldiğinde geri çağrıyı kuyruğa koyar.

Bu modelin sonucu iki tanedir. Bekleyen iş ucuzdur: binlerce açık bağlantı tek süreçte
tutulabilir, çünkü her biri yalnız bir soket ve bir geri çağrıdır. Buna karşılık **hesaplayan
iş pahalıdır**: işlemciyi meşgul eden bir hesap, süresi boyunca kuyruğu kilitler.

## Bloklayan İş: Bir İstek Diğerlerini Ne Kadar Bekletir

Aşağıdaki sunucu üye parolası doğrulamasını iki ayrı yoldan yapar. `dogrula-bloklayan`
anahtar türetmeyi doğrudan çağırır ve sonucu beklerken olay döngüsünü elinde tutar.
`dogrula-havuzda` aynı hesabı çalışma zamanının **iş parçacığı havuzuna** (thread pool)
verir ve döngüyü serbest bırakır. Hesabın kendisi ikisinde de aynıdır.

```js
// tek-surec.mjs — odunc servisi tek surecte; ayni is hem bloklayan hem bloklamayan bicimde
import { createServer } from "node:http";
import { scrypt, scryptSync } from "node:crypto";

const RAFLAR = [{ isbn: "978-0262033848", ad: "Algoritmalara Giris", raf: "R-12" }];

// Anahtar turetme maliyeti: yarim saniyenin uzerinde islemci isi uretecek kadar yuksek.
const MALIYET = { N: 1 << 16, r: 8, p: 8, maxmem: 512 * 1024 * 1024 };

createServer((istek, yanit) => {
  yanit.sendDate = false;
  yanit.setHeader("content-type", "application/json; charset=utf-8");
  const yol = new URL(istek.url, "http://yerel").pathname;

  if (yol === "/uye/dogrula-bloklayan") {
    const ozet = scryptSync("parola", "tuz", 32, MALIYET).toString("hex").slice(0, 16);
    return yanit.writeHead(200).end(JSON.stringify({ ozet, yol }));
  }
  if (yol === "/uye/dogrula-havuzda") {
    return scrypt("parola", "tuz", 32, MALIYET, (hata, anahtar) => {
      if (hata) return yanit.writeHead(500).end(JSON.stringify({ hata: "turetme" }));
      yanit.writeHead(200).end(JSON.stringify({ ozet: anahtar.toString("hex").slice(0, 16), yol }));
    });
  }
  if (yol === "/kitaplar") {
    return yanit.writeHead(200).end(JSON.stringify({ kitaplar: RAFLAR }));
  }
  yanit.writeHead(404).end(JSON.stringify({ hata: "yol_yok" }));
}).listen(8430, "127.0.0.1", () => console.log("tek surec 127.0.0.1:8430 pid=" + process.pid));
```

Ölçüm şu düzendedir: önce ağır istek gönderilir, elli milisaniye sonra dört hafif istek
arkasından gelir. Hafif isteklerin kendi işi neredeyse yoktur; süreleri, yalnızca sıralarını
beklemekten gelir.

```bash
#!/usr/bin/env bash
# tek-surec.mjs'yi baslatir; agir istegin yaninda gelen dort hafif istegin suresini olcer.
node tek-surec.mjs & sunucu=$!
sleep 0.6
curl -sS -o /dev/null http://127.0.0.1:8430/kitaplar          # isinma

olc() {  # $1 = agir ucun yolu
  pidler=()
  curl -sS -o /dev/null -w "agir     %{time_total} s\n" "http://127.0.0.1:8430$1" > agir.txt &
  pidler+=($!)
  sleep 0.05
  for i in 1 2 3 4; do
    curl -sS -o /dev/null -w "hafif-$i  %{time_total} s\n" http://127.0.0.1:8430/kitaplar > "hafif-$i.txt" &
    pidler+=($!)
  done
  wait "${pidler[@]}"
  cat agir.txt hafif-1.txt hafif-2.txt hafif-3.txt hafif-4.txt
}

echo "--- agir is olay dongusunde: /uye/dogrula-bloklayan ---"
olc /uye/dogrula-bloklayan
echo "--- agir is is parcacigi havuzunda: /uye/dogrula-havuzda ---"
olc /uye/dogrula-havuzda

kill "$sunucu"
rm -f agir.txt hafif-*.txt
```

```
tek surec 127.0.0.1:8430 pid=14674
--- agir is olay dongusunde: /uye/dogrula-bloklayan ---
agir     0.635000 s
hafif-1  0.581384 s
hafif-2  0.581601 s
hafif-3  0.580992 s
hafif-4  0.581166 s
--- agir is is parcacigi havuzunda: /uye/dogrula-havuzda ---
agir     0.618827 s
hafif-1  0.001071 s
hafif-2  0.000954 s
hafif-3  0.000756 s
hafif-4  0.001232 s
```

Süreç kimliği ve süreler her çalıştırmada değişir; ağır işin mutlak süresi de makinenin
işlemcisine bağlıdır. Değişmeyen, iki bloktaki hafif isteklerin süreleri arasındaki
büyüklük farkıdır.

Birinci ölçümde hafif istekler yaklaşık **580 milisaniye** beklemiştir. Kendi işleri bir
milisaniyeden azdır; bekledikleri şey, ağır isteğin kalan süresidir. Dördü de neredeyse aynı
anda tamamlanır, çünkü hepsi aynı engelin kalkmasını beklemiş ve engel kalkınca art arda
işlenmiştir.

İkinci ölçümde ağır isteğin süresi değişmemiştir — hesap aynı hesaptır — ama hafif istekler
**bir milisaniyenin** altında yanıtlanmıştır. Aradaki tek fark, hesabın nerede yapıldığıdır.
Havuz sürümünde olay döngüsü hesabı bir iş parçacığına verip serbest kalmış, arada gelen
istekleri normal hızında karşılamış, sonuç hazır olduğunda geri çağrıyı çalıştırmıştır.

## Havuz da Sınırlıdır

İş parçacığı havuzu bloklayan işin çözümü değil, taşındığı yerdir. Havuzun iş parçacığı
sayısı sabittir ve varsayılan değeri küçüktür. Havuza aynı anda kapasitesinden fazla iş
verilirse fazlası kuyruğa girer; bu kez olay döngüsü değil, havuz doyar. Ölçümdeki tek ağır
istek havuzu doldurmadığı için gecikme görünmemiştir.

Bu, ölçeklemenin ilk kuralını verir: **bir sürecin işlemci kapasitesi sabittir.** Hesabı
döngüden havuza taşımak isteklerin birbirini beklemesini azaltır, ama sürecin toplam işlemci
işini artırmaz. Saniyede yapılabilecek anahtar türetme sayısı, işi hangi iş parçacığının
yaptığından bağımsız olarak makinenin çekirdek sayısıyla sınırlıdır.

## Ölçekleme Birimi: Süreç

Tek bir süreç tek bir çekirdeği doyurabilir. Çok çekirdekli bir makinede kalan çekirdekleri
kullanmanın yolu, uygulamanın birden çok kopyasını çalıştırmaktır. **Küme** (cluster)
düzeninde bir **birincil süreç** (primary process) dinleyen soketi açar ve birkaç **işçi
süreç** (worker process) çatallar; gelen bağlantılar işçiler arasında dağıtılır. İşçiler aynı
bağlantı noktasını paylaşır, çünkü soketi açan taraf birincil süreçtir.

```js
// kume.mjs — ayni baglanti noktasini paylasan uc isci surec; her surecin kendi sayaci var
import cluster from "node:cluster";
import { createServer } from "node:http";

const ISCI_SAYISI = 3;

if (cluster.isPrimary) {
  let kapaniyor = false;
  for (let i = 0; i < ISCI_SAYISI; i++) cluster.fork();

  cluster.on("exit", (isci) => {                        // olen iscinin yerine yenisi
    if (kapaniyor) return;
    console.log(`isci oldu pid=${isci.process.pid}, yerine yenisi catallaniyor`);
    cluster.fork();
  });

  process.on("SIGTERM", () => {                         // isciler birincille birlikte iner
    kapaniyor = true;
    for (const isci of Object.values(cluster.workers)) isci.kill();
    process.exit(0);
  });

  console.log(`birincil pid=${process.pid}, isci sayisi=${ISCI_SAYISI}`);
} else {
  let oduncSayaci = 0;                                  // SURECE OZEL bellek: her iscide ayri
  createServer((istek, yanit) => {
    yanit.sendDate = false;
    yanit.setHeader("content-type", "application/json; charset=utf-8");
    if (new URL(istek.url, "http://yerel").pathname === "/odunc") oduncSayaci++;
    yanit.writeHead(200).end(JSON.stringify({ pid: process.pid, oduncSayaci }));
  }).listen(8431, "127.0.0.1");
}
```

Sunucu her yola yanıt verir ve yanıtta kendi süreç kimliğini bildirir; yalnız `/odunc` yolu
sayacı artırır. Bu ayrım, dağılımı sayaç değerini değiştirmeden yoklamayı sağlar.

```bash
#!/usr/bin/env bash
# kume.mjs'yi baslatir; 12 istegin isciler arasindaki dagilimini, sayaclarin ayrismasini
# ve olen iscinin yerine yenisinin gelmesini gosterir.
node kume.mjs & kume=$!
sleep 0.8

echo "--- 12 odunc istegi: hangi isci kac kez yanitladi ---"
for i in $(seq 12); do curl -sS http://127.0.0.1:8431/odunc; echo; done |
  grep -o '"pid":[0-9]*' | sort | uniq -c

echo "--- ayni uce dort istek: her iscinin sayaci kendi belleginde ---"
for i in 1 2 3 4; do curl -sS http://127.0.0.1:8431/odunc; echo; done

echo "--- bir isci oldurulur, yerine yenisi gelir ---"
kurban=$(curl -sS http://127.0.0.1:8431/durum | sed -n 's/.*"pid":\([0-9]*\).*/\1/p')
echo "oldurulen isci pid=$kurban"
kill -9 "$kurban"
sleep 0.8
for i in $(seq 6); do curl -sS http://127.0.0.1:8431/durum; echo; done |
  grep -o '"pid":[0-9]*' | sort -u

kill "$kume"
```

```
birincil pid=3068, isci sayisi=3
--- 12 odunc istegi: hangi isci kac kez yanitladi ---
   4 "pid":3070
   4 "pid":3071
   4 "pid":3072
--- ayni uce dort istek: her iscinin sayaci kendi belleginde ---
{"pid":3072,"oduncSayaci":5}
{"pid":3070,"oduncSayaci":5}
{"pid":3071,"oduncSayaci":5}
{"pid":3072,"oduncSayaci":6}
--- bir isci oldurulur, yerine yenisi gelir ---
oldurulen isci pid=3070
isci oldu pid=3070, yerine yenisi catallaniyor
"pid":3071
"pid":3072
"pid":3098
```

Süreç kimlikleri her çalıştırmada başkadır. Sabit olan üç gözlemdir.

**Dağılım eşittir.** On iki istek üç işçiye dörder düşmüştür; birincil süreç bağlantıları
sırayla dağıtmıştır. İstemci tarafında hiçbir şey değişmemiştir: aynı adrese, aynı bağlantı
noktasına istek yapılmıştır. Kaç sürecin çalıştığı istemciye görünmez.

**Ölen işçinin yerine yenisi gelir.** Bir işçi zorla sonlandırıldığında birincil süreç bunu
görür ve yeni bir işçi çatallar; son listede eski kimliklerden biri yoktur, yerine yeni bir
kimlik vardır. Bu, sürecin ölçekleme biriminin yanı sıra **yalıtım birimi** olduğunu da
gösterir: bir işçinin çökmesi diğerlerini etkilemez ve hizmet kesilmez.

## Süreç Belleği Ölçekleme Sınırıdır

Çıktıdaki en öğretici satırlar sayaç satırlarıdır. On iki isteğin ardından her işçinin
sayacı dörttür; sonraki dört istek dağıtıldığında değerler `5`, `5`, `5` ve `6` olarak
okunur. Hiçbir işçi toplam sayıyı bilmez, çünkü sayaç **süreç belleğinde** yaşar ve süreçler
belleklerini paylaşmaz.

Kütüphane uygulamasında bunun karşılığı doğrudandır. Ödünç sayısı, oturum bilgisi, geçici
rezervasyon listesi, hız sınırı sayacı — hepsi süreç belleğinde tutulduğunda ölçekleme
anında bozulur. Üyenin oturum açtığı istek bir işçiye, sonraki isteği başka bir işçiye
düşer ve ikinci işçi oturumu tanımaz.

Bunun sonucu, çalışma zamanı modelinin uygulama tasarımına koyduğu bağlayıcı kuraldır:
**süreç durumsuz olmalıdır.** İstekler arasında yaşaması gereken her değer süreç dışına,
paylaşılan bir depoya taşınır. Süreç yalnız o isteğin süresince yaşayan değerleri kendi
belleğinde tutar. Bu kural sağlandığında süreç sayısını artırmak bir yapılandırma kararına
iner; sağlanmadığında ikinci süreci başlatmak hatalara yol açar.

Aynı kural sürecin ölümlü olmasından da gelir. Ölen işçinin belleği onunla birlikte gider;
yerine gelen işçi boş bellekle başlar. Süreç belleğinde tutulan bir değer, yalnız ölçekleme
sırasında değil, her yeniden başlatmada kaybolur.

## Özet

- Uygulama kodu tek bir iş parçacığında çalışır; bekleyen girdi-çıktı ucuzdur, işlemciyi
  meşgul eden hesap ise süresi boyunca kuyruğu kilitler.
- Ölçümde olay döngüsünü bloklayan yarım saniyelik bir hesap, yanında gelen dört hafif
  isteği yaklaşık 580 milisaniye bekletmiştir; aynı hesap iş parçacığı havuzuna
  verildiğinde hafif istekler bir milisaniyenin altında yanıtlanmıştır.
- İş parçacığı havuzu işi taşır, kapasiteyi artırmaz; bir sürecin işleyebileceği toplam
  işlemci işi sabittir.
- Ölçekleme birimi süreçtir: birincil süreç dinleyen soketi açar, işçi süreçler aynı
  bağlantı noktasını paylaşır, on iki istek üç işçiye dörder dağılmıştır ve ölen işçinin
  yerine yenisi çatallanmıştır.
- Süreçler bellek paylaşmadığı için süreç belleğindeki sayaç işçiler arasında ayrışır;
  istekler arasında yaşaması gereken durum süreç dışına, paylaşılan bir depoya taşınmalıdır.

## Sonraki Adım

Bu ders sürecin dışını ölçtü: kaç kopya çalışır, istek hangi kopyaya düşer, kopyalar neyi
paylaşmaz. Sürecin içinde ise tek bir isteğin geçtiği bir yol vardır ve bu yol henüz tek bir
kutu gibi ele alındı. Bağlantı kabul edildiği andan yanıtın son baytı yazıldığı ana kadar
kaç ayrı aşama vardır, her aşama ne kadar sürer ve bir aşamada verilen karar sonrakini nasıl
kısıtlar? Sonraki ders isteğin yaşam döngüsünü aşamalara böler; her aşamaya zaman damgası
koyup sırayı ve süreleri yazdırır.
