---
title: 'İşçi Süreçleri'
source: 'https://academia.sh/tr/kurslar/eszamansiz-isleme/isci-surecleri'
course: 'Önbellekleme, Kuyruklar ve Eşzamansız İşleme'
language: tr
updated: '2026-08-17T18:06:46+00:00'
license: 'CC BY-SA 4.0'
---

# İşçi Süreçleri

İşin mesajdan ayrı bir ölçekleme birimi olarak ele alınması: iş havuzunun kurulması, işçi sayısının işlem hacmine etkisinin ölçülmesi, düşen işçinin gözetimle yerine konması ve zarif kapanma.

Bir önceki ders mesajın kendisini dayanıklı hale getirmeyi tamamladı: deneme sayacı,
üstel geri çekilme, sarsıntı ve ölü mektup kuyruğu. Artık bir mesaj kaybolmuyor,
başarısız olduğunda yeniden deneniyor, düzelmiyorsa yalıtılıyor.

Bu güvencelerin tamamı mesajın kendisiyle ilgiliydi. Mesajı **işleyen taraf** ise hâlâ
adsız bir "tüketici" olarak duruyor. Oysa o taraf bir süreçtir: belleği vardır, çöker,
yeniden başlar, kapatılır. Kaç tane olacağı bir karardır ve bu karar servisin saatte kaç
iş bitireceğini belirler. Bu ders tüketiciyi bir **ölçekleme birimi** (unit of scaling)
olarak ele alır ve o birimin kaç tane olması gerektiğini ölçmeyi öğretir.

## İşçi Nedir

**İşçi** (worker), kuyruktan iş alıp işleyen bağımsız bir çalıştırma birimidir. Ayrı bir
süreç, ayrı bir iş parçacığı ya da ayrı bir kap olabilir; ortak yanı, **kendi başına
düşebilmesi ve kendi başına çoğaltılabilmesidir**.

İşçiyi istek işleyen sunucudan ayıran üç özellik vardır. İşçinin bir istemcisi yoktur;
kimse yanıtı beklemez, dolayısıyla iş uzun sürebilir. İşçinin iş kaynağı ağ dinleyicisi
değil kuyruktur; yükü kabul etmeyi reddedemez, yalnız yavaş tüketebilir. Ve işçi
**yatay** çoğaltılır: aynı kuyruktan besleneceği için ikinci bir işçi eklemek herhangi
bir eşgüdüm gerektirmez.

Kütüphane ödünç servisinde arka plana taşınan işler bellidir: gecikme bildirimlerinin
gönderilmesi, aylık ödünç raporunun üretilmesi, yüklenen kapak görselinin işlenmesi, şube
stok özetinin yeniden hesaplanması. Bu derste ilkini örnek alıyoruz.

## İşin Kendisi

İşçi süreci tek bir sorumluluk taşır: gelen bir iş tanımını çalıştırmak ve sonucu
bildirmek. Kuyruğun nerede durduğunu, işin kaç kez denendiğini, kaç işçi olduğunu
bilmez.

```js
// isci.mjs — bir isi alir, yapar, sonucu bildirir; havuz.mjs onu fork eder
if (!process.send) {
  console.log("bu dosya tek basina calismaz; havuz.mjs onu fork eder");
  process.exit(0);
}

const SIRA = Number(process.argv[2]);            // havuzun isciye verdigi sira numarasi
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));

// Gecikme bildirimi gonderimi: disariya bir cagri yapan, 100 ms suren is.
async function bildirimGonder(is) {
  if (is.bozuk) process.exit(1);                 // is surecin kendisini dusuruyor
  await bekle(100);
  return `uye ${is.uyeId} bilgilendirildi`;
}

process.on("message", async (is) => {
  const sonuc = await bildirimGonder(is);
  process.send({ tur: "bitti", isId: is.isId, sira: SIRA, sonuc });
});

process.on("SIGTERM", () => process.exit(0));    // havuz kapatirken is yarida kalmaz
```

```
bu dosya tek basina calismaz; havuz.mjs onu fork eder
```

Dosya doğrudan çalıştırıldığında `process.send` tanımsızdır ve işçi bunu söyleyip çıkar.
Anlamlı çalıştığı tek durum, bir üst sürecin onu çatallamasıdır.

`bildirimGonder` içindeki 100 milisaniyelik bekleme, dışarıya yapılan bir çağrının
yerine geçer. İşin **girdi–çıktı bağımlı** (I/O-bound) olduğunu belirtir: süre işlemci
kullanılarak değil, cevabı beklenerek geçer. Bu ayrım birazdan işçi sayısı kararında
belirleyici olacak.

## Havuz ve Çekme Temelli Dağıtım

İşleri işçilere bölüştürmenin iki yolu vardır. **İtme temelli** dağıtımda üst süreç
işleri baştan paylaştırır: on iki iş, dört işçi, her birine üç iş. Bu yol yalnız işlerin
süreleri eşitse verimlidir; biri uzun sürerse o işçi çalışırken diğerleri boş bekler.
**Çekme temelli** (pull-based) dağıtımda ise işçi bir işi bitirdiğinde sıradakini ister.
Yük kendiliğinden dengelenir.

Aşağıdaki havuz çekme temellidir: `dagit` her çağrıldığında kuyruğun başındaki işi
gönderir, kuyruk boşaldığında işçiyi kapatır.

```js
// havuz.mjs — sabit sayida isci fork eder, is kuyrugunu onlara dagitir, sureyi olcer
import { fork } from "node:child_process";

const ISLER = Array.from({ length: 12 }, (_, i) => ({
  isId: i + 1, oduncId: 500 + i, uyeId: 100 + i,
}));

function havuzCalistir(isciSayisi, isler) {
  return new Promise((coz) => {
    const kuyruk = [...isler];
    const bitenler = [];
    const isciler = [];
    const basla = performance.now();

    const dagit = (isci) => {
      const is = kuyruk.shift();
      if (is) isci.send(is);
      else isci.kill("SIGTERM");
    };

    for (let s = 1; s <= isciSayisi; s++) {
      const isci = fork(new URL("isci.mjs", import.meta.url), [String(s)]);
      isci.on("message", (ileti) => {
        bitenler.push(ileti);
        if (bitenler.length === isler.length) {
          const sure = performance.now() - basla;
          for (const i of isciler) i.kill("SIGTERM");
          coz({ sure, bitenler });
        } else dagit(isci);
      });
      isciler.push(isci);
      dagit(isci);
    }
  });
}

for (const n of [1, 2, 4]) {
  const { sure, bitenler } = await havuzCalistir(n, ISLER);
  const dilim = Math.floor(sure / 100);   // her is 100 ms surer; dilim = 100 ms
  console.log(`isci=${n}  is=${bitenler.length}  sure=${dilim} dilim`);
}
```

```
isci=1  is=12  sure=12 dilim
isci=2  is=12  sure=6 dilim
isci=4  is=12  sure=3 dilim
```

Süre, tek bir işin süresi olan 100 milisaniyelik **dilimler** cinsinden yazdırılıyor;
böylece sonuç makinenin hızından bağımsız okunabilir hale gelir. Dilim sayısı aşağı
yuvarlanır, dolayısıyla süreç başlatma gideri sayıya yansımaz. Yavaş bir makinede
dilim sayıları bir üste kayabilir.

Okunacak şey oran: on iki iş bir işçiyle on iki dilim, iki işçiyle altı, dört işçiyle üç
dilim sürüyor. İşçi sayısı iki katına çıktığında süre yarıya iniyor. Bu, işin
**bölünebilir** olmasının doğrudan sonucudur: bir üyeye bildirim göndermek başka bir
üyeye bildirim göndermeyi beklemez.

### Ölçeklemenin Sınırı

Oranın sonsuza kadar sürmediği iki nokta vardır.

Birincisi işin türüdür. Yukarıdaki iş girdi–çıktı bağımlıydı: işçi çoğu zaman cevabı
bekliyordu, o yüzden işçi sayısı çekirdek sayısını aşsa da kazanç sürdü. İş **işlemci
bağımlı** (CPU-bound) olsaydı — örneğin kapak görselinin yeniden boyutlandırılması —
kazanç çekirdek sayısında durur, o noktadan sonra işçi eklemek yalnız bağlam değiştirme
gideri üretirdi.

İkincisi paylaşılan kaynaktır. On iki işçi aynı veritabanı bağlantı havuzundan besleniyorsa
darboğaz işçi sayısı değil bağlantı sayısıdır. Bildirim gönderimi tek bir dış servise
gidiyorsa darboğaz o servisin kabul ettiği eşzamanlılıktır. İşçi sayısını artırmak bu iki
durumda toplam süreyi kısaltmaz, yalnız beklemeyi işçilere dağıtır.

Bu yüzden işçi sayısı bir tahmin değil, ölçüm sonucudur: sayı artırılır, toplam süre
ölçülür, süre düşmeyi bıraktığı yerde durulur.

## Düşen İşçi ve Gözetim

İşçi bağımsız bir süreç olduğu için düşebilir: bellek biter, işletim sistemi onu
sonlandırır, işlediği iş sürecin kendisini çökertir. Havuzun buna karşı iki görevi
vardır. Düşen işçinin **yerine yenisini koymak** ve o işçinin **üzerindeki işi
kaybetmemek**.

İkincisi kritik olandır. İşçi çökerken hiçbir şey bildirmez; havuz yalnız `exit`
olayını görür. O anda işçinin hangi işi taşıdığı bilinmiyorsa iş sessizce kaybolur. Bu
yüzden havuz her işçinin üzerindeki işi ayrı bir eşlemede tutar.

```js
// dayanikli.mjs — isci dustugunde yerine yenisi konur, uzerindeki is geri kuyruga alinir
import { fork } from "node:child_process";

const ISLER = [
  { isId: 1, oduncId: 500, uyeId: 100 },
  { isId: 2, oduncId: 501, uyeId: 101 },
  { isId: 3, oduncId: 502, uyeId: 102, bozuk: true },
  { isId: 4, oduncId: 503, uyeId: 103 },
  { isId: 5, oduncId: 504, uyeId: 104 },
];
const EN_COK_DENEME = 2;

const kuyruk = [...ISLER];
const deneme = new Map();                        // isId -> kac kez denendi
const bitenler = [], oluMektup = [];
const uzerindeki = new Map();                    // isci -> o an isledigi is
let acik = 0, sonrakiSira = 1;

function isciYarat() {
  const isci = fork(new URL("isci.mjs", import.meta.url), [String(sonrakiSira++)]);
  acik++;
  isci.on("message", (m) => { uzerindeki.delete(isci); bitenler.push(m.isId); dagit(isci); });
  isci.on("exit", () => {
    acik--;
    const is = uzerindeki.get(isci);             // dolu ise: isci is uzerindeyken coktu
    if (is) {
      uzerindeki.delete(isci);
      if (deneme.get(is.isId) < EN_COK_DENEME) kuyruk.push(is);
      else oluMektup.push(is.isId);
      isciYarat();                               // coken iscinin yerine yenisi
    }
    if (acik === 0) rapor();
  });
  dagit(isci);
}

function dagit(isci) {
  const is = kuyruk.shift();
  if (!is) return isci.kill("SIGTERM");
  deneme.set(is.isId, (deneme.get(is.isId) ?? 0) + 1);
  uzerindeki.set(isci, is);
  isci.send(is);
}

function rapor() {
  console.log("biten isler       :", bitenler.sort((a, b) => a - b).join(", "));
  console.log("olu mektup kuyrugu:", oluMektup.join(", ") || "-");
  console.log("yaratilan isci    :", sonrakiSira - 1);
  console.log("3 numarali deneme :", deneme.get(3));
}

isciYarat();
isciYarat();
```

```
biten isler       : 1, 2, 4, 5
olu mektup kuyrugu: 3
yaratilan isci    : 4
3 numarali deneme : 2
```

Üç numaralı iş işçiyi çökertiyor. Havuz `exit` olayında işi geri kuyruğa alıyor ve
düşen işçinin yerine yenisini yaratıyor. İkinci deneme de aynı sonucu verdiği için iş
deneme sınırını aşıyor ve önceki derste tanıtılan ölü mektup kuyruğuna gidiyor. Diğer
dört iş etkilenmiyor; toplamda dört işçi yaratılıyor, ikisi başlangıçtaki, ikisi
çökenlerin yerine gelen.

Buradaki kalıbın adı **gözetim**dir (supervision): işi yapan birim ile onun yaşamını
yöneten birim ayrılır. İşçi kendi hatasını onarmaya çalışmaz, çöker; havuz onarır. Bu
ayrım işçinin kodunu belirgin ölçüde yalınlaştırır.

Gözetimin gizli bir maliyeti vardır. Geri kuyruğa alınan iş, çökme anında **kısmen
yapılmış** olabilir: bildirim gönderilmiş, ancak gönderildiği kaydedilmemiş olabilir. Bu
yüzden yeniden denenen her iş, Web API Tasarımı kursunda tanıtılan tekillik anahtarıyla
korunmalıdır; aksi hâlde gözetim, üyeye iki bildirim gönderme pahasına dayanıklılık satın
alır.

## Düzgün Kapanma

Havuz işçiyi `SIGTERM` ile kapatıyor. İşçi de bu sinyali karşılayıp çıkıyor. Sinyalin
karşılanmaması durumunda süreç varsayılan davranışla anında sonlanır ve üzerindeki iş
yarıda kalır.

**Zarif kapanma** (graceful shutdown) üç adımdır: yeni iş almayı bırak, elindeki işi
bitir, sonra çık. Uygulamada üst süreç önce `SIGTERM` gönderir, işçinin bitirmesi için
sınırlı bir süre tanır, süre dolarsa `SIGKILL` ile zorlar. Bu ikinci sinyal işçi
tarafından karşılanamaz; bu yüzden tanınan sürenin en uzun işten büyük olması gerekir.

Dağıtım araçlarının işçi kaplarını durdururken izlediği sıra da budur. İşçinin sinyali
karşılamaması, her yeniden dağıtımda yarıda kalmış iş üretir.

## Özet

- İşçi, kuyruktan iş alıp işleyen bağımsız bir çalıştırma birimidir; kendi başına düşebilir ve kendi başına çoğaltılabilir olması onu bir ölçekleme birimi yapar.
- Çekme temelli dağıtımda işçi bir işi bitirdiğinde sıradakini ister; yük, işlerin süreleri eşit olmasa da dengelenir.
- İşçi sayısının kazancı ölçülerek bulunur: girdi–çıktı bağımlı işte çekirdek sayısının ötesine geçebilir, işlemci bağımlı işte orada durur, paylaşılan kaynak varsa çok daha erken durur.
- Gözetim, düşen işçinin yerine yenisini koyar ve üzerindeki işi geri kuyruğa alır; bunun bedeli işin kısmen yapılmış olma ihtimalidir, bu yüzden yeniden denenen iş tekillik anahtarıyla korunur.
- Zarif kapanma sırası yeni iş almayı bırakmak, elindekini bitirmek ve çıkmaktır; tanınan süre en uzun işten kısa olursa her kapanış yarım iş üretir.

## Sonraki Adım

Bu derste işler dışarıdan geldi: havuz elindeki listeyi dağıttı. Arka plan işlerinin
büyük bölümü ise dışarıdan gelmez, **takvimden** gelir. Gecikme bildirimi her sabah
altıda, aylık ödünç raporu her ayın birinde, şube stok özeti her on beş dakikada bir
üretilir. Bu işleri bir zaman ölçütüne bağlamak tek satırlık bir tanım gibi görünür, ama
altında iki soru vardır: yineleme nasıl tanımlanır ve aynı görevin iki koşumu üst üste
bindiğinde ne olur. Sonraki ders yineleyen görev tanımını kurar ve çakışmayı ölçerek
önler.
