---
title: 'Kuyruklar ve İşler'
source: 'https://academia.sh/tr/kurslar/web-uygulama-cercevesi/kuyruklar-ve-isler'
course: 'Web Uygulama Çerçevesi'
language: tr
updated: '2026-10-21T05:03:39+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'İş sınıfını, kuyruğa koymayı ve işçiyi birbirinden ayırır'
    - 'Kuyruğa konan işin yanıt anında ertelendiğini, eşzamanlı sürücüde aynı işin koştuğunu ölçer'
    - 'Yeniden deneme hakkının nasıl tüketildiğini ve başarısız işlerin nereye düştüğünü gösterir'
---

# Kuyruklar ve İşler

İş sınıfı, kuyruğa koyma ve işçi; kuyruğa konan işin yanıt anında ertelenmesi, eşzamanlı sürücüde aynı işin koşması; ilk denemede başarısız olan işin yeniden denenmesi ve deneme hakkı bitince başarısız işler listesine düşmesi.

Kimlik Doğrulama ve Yetkilendirme dersinde kapı eylemi atladı ve yanıt hemen döndü. Kapı geçirdiğinde ise eylem koşar ve eylemin içinde yanıtı geciktirecek bir iş olabilir: bir görselin küçük resmini üretmek, bir raporu hazırlamak. Bu ders şunu sorar: **bu iş yanıtı bekletmeli mi ve beklemiyorsa kim, ne zaman koşturur?**

Önce adlandırma. Bu derste model **aynı süreçtedir**: ayrı bir sunucu, ayrı bir süreç ya da ağ yoktur. "Kuyruk" bir dizidir ve **işçi (worker)** bu diziden işleri alıp çalıştıran `isle()` çağrısının adıdır. İsteğin yanıtı döndükten sonra bu çağrı yapılır. Gerçek bir sistemde işçi ayrı bir süreç olurdu; burada ayrım yalnız yanıtın işi bekleyip beklemediğidir. Benzer bir soruyu JVM, Eşzamanlılık ve Başarım kursunda (M08/K04) görmüştük, ama orada adımı kimin yürüttüğü arandı: hangi iş parçacığının çalıştırdığı ölçüldü. Burada soru o değil; yanıtın işi bekleyip beklemediği.

**US2.** Kuyruk bir dizidir, işçi senkron bir döngüdür. Zaman aşımı, eşzamanlı işçiler, kilitleme ve iş sırasının kalıcılığı modellenmez; kuyruk sürücüsü adı kullanılmaz. Her deneme bir sayaçla izlenir ve `sleep` çağrılmaz: yeniden deneme aralığı gerçek zaman değil, deneme numarasıdır. İş içinde fırlatılan istisna işçide yakalanır ve yanıta ulaşmaz.

## Ölçüm Çekirdeği

Kurs boyunca her derste aynı ölçüm kullanılır. `kostu.php` beklenen bir parçanın bu istekte koşup koşmadığını kaydeder; dosya her derste birebir verilir ve çıktı bloğu yoktur, yalnız diske yazılır. `Gunluk` üç liste tutar: isteğin yoluna konanlar, istek içinde çağrılanlar ve isteğin dışına bırakılanlar. `sinifla()` bunlara sırayla bakar. **Karar sırası `sinifla()`'nın kendisidir:** koştu, ertelendi, atlandı, bağlanmadı. Parçayı geliştirici adlandırır; ölçülen, geliştiricinin bu istekte koşmasını beklediği parçalardır.

```php
<?php
// kostu.php — beklenen bir parça bu istekte koştu mu?
declare(strict_types=1);

final class Gunluk {
    public static array $yol = [];         // isteğin yoluna konan parçalar
    public static array $kostu = [];       // istek içinde çağrılanlar
    public static array $ertelenen = [];   // isteğin dışına bırakılanlar
    public static function yeniIstek(): void { self::$yol = self::$kostu = self::$ertelenen = []; }
    public static function yola(string $parca): void { self::$yol[] = $parca; }
    public static function kostu(string $parca): void { self::$kostu[] = $parca; }
    public static function ertele(string $parca): void { self::$ertelenen[] = $parca; }
}

function sinifla(string $parca): string {
    return match (true) {
        in_array($parca, Gunluk::$kostu, true) => 'koştu',
        in_array($parca, Gunluk::$ertelenen, true) => 'ertelendi',
        in_array($parca, Gunluk::$yol, true) => 'atlandı',
        default => 'bağlanmadı',
    };
}
```

Bu ders çekirdeğin üç çağrısını kullanır. Küçük resim işi yola konur (`yola()`), kuyruğa gönderilirken `ertele()` ile isteğin dışına bırakıldığı işaretlenir, eşzamanlı sürücüde ise `kostu()` ile çağrıldığı işaretlenir.

## İş Sınıfı, Sürücü ve İşçi

`isler.php` dilimin tamamıdır. `IsSinifi` bir işin sözleşmesidir: bir `ad()`, bir `calistir()` ve kaç kez denendiğini sayan `deneme` alanı. `KucukResimIsi` tek bir yazı kimliği alır ve `calistir()` çağrıldığında kimliği bir diziye ekler; diziye yazılmış olması işin koştuğunun kanıtıdır.

İşin nasıl gönderildiğine **sürücü (driver)** karar verir. `Surucu` arayüzünün tek yöntemi `gonder()` vardır. `EszamanliSurucu` işi kuyruğa koymaz, `Gunluk::kostu()` ile işaretleyip hemen çalıştırır. `BellekSurucusu` ise `Gunluk::ertele()` ile işaretleyip işi bir diziye ekler ve yanıtı döndürür; iş orada bekler. Bekleyen işleri `isle()` çalıştırır: her işi diziden çeker, denemeyi bir artırır, `calistir()` der ve istisnayı yakalarsa deneme hakkı kalmışsa işi dizinin sonuna geri koyar, kalmamışsa `basarisiz` listesine atar.

`YazilarDenetleyicisi::kaydet()` ise sürücüyü bilmez. Yalnız `Surucu` arayüzüne konuşur: bir iş nesnesi üretir, `gonder()` der ve 201 döndürür. Denetleyicinin kodu iki sürücüde de aynıdır; değişen, sürücünün işi ne yaptığıdır.

```php
<?php
// isler.php — iş sınıfı, iki sürücü ve işçi
declare(strict_types=1);
require_once 'kostu.php';

abstract class IsSinifi {
    public int $deneme = 0;
    abstract public function ad(): string;
    abstract public function calistir(): void;
}

final class KucukResimIsi extends IsSinifi {
    public static array $uretilen = [];
    public function __construct(private int $yaziId) {}
    public function ad(): string { return 'kucukResim'; }
    public function calistir(): void { self::$uretilen[] = $this->yaziId; }
}

interface Surucu {
    public function gonder(IsSinifi $is): void;
}

final class EszamanliSurucu implements Surucu {
    public function gonder(IsSinifi $is): void {
        Gunluk::kostu($is->ad());
        $is->calistir();
    }
}

final class BellekSurucusu implements Surucu {
    private array $bekleyen = [];
    public array $basarisiz = [];
    public function __construct(private int $denemeHakki = 3) {}
    public function gonder(IsSinifi $is): void {
        Gunluk::ertele($is->ad());
        $this->bekleyen[] = $is;
    }
    public function bekleyen(): int { return count($this->bekleyen); }
    public function isle(): array {            // işçi: istekten bağımsız koşar
        $gunluk = [];
        while ($is = array_shift($this->bekleyen)) {
            $is->deneme++;
            try {
                $is->calistir();
                $gunluk[] = "{$is->ad()} deneme {$is->deneme}: koştu";
            } catch (Throwable $hata) {
                $gunluk[] = "{$is->ad()} deneme {$is->deneme}: " . get_class($hata);
                if ($is->deneme < $this->denemeHakki) $this->bekleyen[] = $is;
                else $this->basarisiz[] = $is;
            }
        }
        return $gunluk;
    }
}

final class YazilarDenetleyicisi {
    public function __construct(private Surucu $surucu) {}
    public function kaydet(int $yaziId): array {
        Gunluk::yeniIstek();
        Gunluk::yola('kaydet');
        Gunluk::yola('kucukResim');
        Gunluk::kostu('kaydet');
        $this->surucu->gonder(new KucukResimIsi($yaziId));
        return [201, 'kaydedildi'];
    }
}
```

## Kuyruğa Konan İş

İlk ölçüm iki parça sorar: `kaydet` (denetleyicinin kendi adımı) ve `kucukResim` (kuyruğa konan iş). Sürücü kuyruktur. Yanıt döndükten sonra `isle()` çağrılır ve işçi çalışır. Karşıt ölçüm aynı denetleyiciyi eşzamanlı sürücüyle koşturur.

```php
<?php
// kuyruk.php — aynı iş iki sürücüyle
require_once 'isler.php';

function olc(string $ad, Surucu $surucu): void {
    KucukResimIsi::$uretilen = [];
    $yanit = (new YazilarDenetleyicisi($surucu))->kaydet(7);
    printf("%s -> %d %s\n", $ad, $yanit[0], $yanit[1]);
    foreach (['kaydet', 'kucukResim'] as $parca) printf("  %-11s %s\n", $parca, sinifla($parca));
    echo '  yolda mı: ', in_array('kucukResim', Gunluk::$yol, true) ? 'evet' : 'hayır', "\n";
    echo '  yanıt anında üretilen: ', count(KucukResimIsi::$uretilen), "\n";
}

$kuyruk = new BellekSurucusu();
olc('kuyruk', $kuyruk);
echo '  bekleyen iş: ', $kuyruk->bekleyen(), "\n";
foreach ($kuyruk->isle() as $satir) echo '  işçi: ', $satir, "\n";
echo '  işçiden sonra üretilen: ', count(KucukResimIsi::$uretilen), "\n";

olc('eşzamanlı', new EszamanliSurucu());
```

```
kuyruk -> 201 kaydedildi
  kaydet      koştu
  kucukResim  ertelendi
  yolda mı: evet
  yanıt anında üretilen: 0
  bekleyen iş: 1
  işçi: kucukResim deneme 1: koştu
  işçiden sonra üretilen: 1
eşzamanlı -> 201 kaydedildi
  kaydet      koştu
  kucukResim  koştu
  yolda mı: evet
  yanıt anında üretilen: 1
```

Bu dersin gözlemi ilk blokta. Yanıt 201 olarak döndü, ama `kucukResim` **ertelendi**: yola konmuştu, isteğin içinde çağrılmadı, isteğin dışına bırakıldı. "Yanıt anında üretilen" sayısı 0'dır, yani küçük resim henüz yoktur; bekleyen iş sayısı 1'dir. İşçi `isle()` ile çalıştığında iş **ilk denemede koştu** ve üretilen sayı 1 oldu. Yanıtı alan istemci küçük resmi beklemedi.

Karar sırası burada işe yarar. `kucukResim` yolda olduğu için `yolda mı` satırı `evet` der; bu satır tek başına işin koştuğunu söylemez. `sinifla()` önce `kostu` listesine bakar, orada bulamaz; sonra `ertelenen` listesine bakar ve bulur. "Yolda" olmak ile "koşmak" ayrı gözlemlerdir ve ertelenen parça tam aralarındadır.

## Karşıt: Eşzamanlı Sürücü

İkinci blok aynı denetleyiciyi `EszamanliSurucu` ile koşturur. Aynı iş, aynı `gonder()` çağrısı; `kucukResim` bu kez **koştu** ve yanıt anında üretilen sayı 1'dir. İstemci yanıtını işin bitmesinden sonra alır. Kod aynıdır, tek değişen sürücüdür. Bu, kuyruğun işi küçültmediğini, yalnız işin koştuğu anı isteğin dışına taşıdığını gösterir.

Ayrımı bir cümleyle koymak gerekirse: M08/K04'te adımı kimin yürüttüğünü aradık; burada adımı kimin yürüttüğü değil, yanıtın onu bekleyip beklemediği önemlidir. Eşzamanlı sürücüde yanıt bekler, kuyrukta beklemez.

## Sınırlayıcı Ölçüm

Kuyruğun işi ertelemesi bir bedeli de getirir: iş yanıttan sonra koştuğu için hata istemciye ulaşmaz, işçide kalır. Bu yüzden yeniden deneme ve başarısız işler listesi gerekir. Aşağıdaki ölçüm iki iş kurar. Birincisi yalnız ilk denemede istisna fırlatır; ikincisi ilk beş denemede fırlatır. Deneme hakkı 3'tür.

```php
<?php
// deneme.php — ilk denemede başarısız olan iş
require_once 'isler.php';

final class KirilganIs extends IsSinifi {
    public function __construct(private int $basarisizDeneme) {}
    public function ad(): string { return 'kirilgan'; }
    public function calistir(): void {
        if ($this->deneme <= $this->basarisizDeneme) throw new RuntimeException('geçici hata');
    }
}

foreach ([[1, 3], [5, 3]] as [$basarisiz, $hak]) {
    Gunluk::yeniIstek();
    $kuyruk = new BellekSurucusu($hak);
    $kuyruk->gonder(new KirilganIs($basarisiz));
    echo "ilk $basarisiz deneme başarısız, hak $hak\n";
    echo '  istekte: ', sinifla('kirilgan'), "\n";
    foreach ($kuyruk->isle() as $satir) echo '  işçi: ', $satir, "\n";
    echo '  başarısız işler: ', count($kuyruk->basarisiz), "\n";
}
```

```
ilk 1 deneme başarısız, hak 3
  istekte: ertelendi
  işçi: kirilgan deneme 1: RuntimeException
  işçi: kirilgan deneme 2: koştu
  başarısız işler: 0
ilk 5 deneme başarısız, hak 3
  istekte: ertelendi
  işçi: kirilgan deneme 1: RuntimeException
  işçi: kirilgan deneme 2: RuntimeException
  işçi: kirilgan deneme 3: RuntimeException
  başarısız işler: 1
```

İki iş de istekte **ertelendi**: yanıt hiçbir durumda hatayı görmedi. İlk iş birinci denemede `RuntimeException` fırlattı, işçi onu yakaladı, hakkı bitmediği için dizinin sonuna geri koydu ve ikinci denemede iş **koştu**. Başarısız işler listesi boş kaldı. İkinci iş üç denemede de başarısız oldu. Üçüncü denemeden sonra hak kalmadığı için geri konmadı ve `basarisiz` listesine düştü: listede 1 iş vardır. Beşinci deneme hiç yapılmadı.

Sayılar şunu söyler: yeniden deneme geçici hatalar içindir ve hata sürerse sonsuza dek denemez; sınırlı bir hak vardır ve hak bitince iş görünür bir yere, başarısız işler listesine düşer. O liste boş değilse birinin bakması gerekir. Yanıtın 201 dönmesi yalnız yazının kaydedildiğini söyler; küçük resim işinin başarılı olduğunu göstermez, o iş yalnız kuyruğa konmuştur.

## Özet

- Bu dersin ölçü sınıfı **ertelendi**: denetleyici küçük resim işini kuyruğa koydu, yanıt 201 döndü, iş yanıt anında çağrılmadı ve işçide koştu.
- Model aynı süreçtedir; işçi `isle()` çağrısının adıdır ve yanıttan sonra çalışır.
- Karşıt: eşzamanlı sürücüde aynı iş **koştu** ve yanıt onu bekledi; denetleyici kodu değişmedi, yalnız sürücü değişti.
- Sınırlayıcı ölçümde ilk denemede başarısız olan iş ikinci denemede koştu; üç denemede de başarısız olan iş başarısız işler listesine düştü.
- Kuyruğa konmuş olmak başarılı olmak değildir: 201 yazının kaydedildiğini söyler, kuyruktaki işin sonucunu söylemez.

## Sonraki Adım

Küçük resim işi kuyruğa girdi, ama denetleyici onu adıyla bildi: `new KucukResimIsi(...)`. Yeni bir yan etki gerektiğinde, örneğin yazı kaydedilince bir arama dizinini güncellemek, denetleyiciye yeni bir satır eklemek gerekir. Olaylar ve Dinleyiciler dersi bu bağı gevşetir: denetleyici bir olay yayar, dinleyici onu işler. Peki olay adı dinleyicinin kayıtlı olduğu adla uyuşmazsa ne olur?
