---
title: 'Süreç Yöneticisi ve Web Sunucusu'
source: 'https://academia.sh/tr/kurslar/php-uretim/surec-yoneticisi-ve-web-sunucusu'
course: "PHP'de Kalite, Başarım ve Dağıtım"
language: tr
updated: '2026-10-21T05:03:36+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Web sunucusu, süreç yöneticisi ve işçi süreç kavramlarını ayırt edebilmek'
    - 'Yerleşik web sunucusunu iki işçiyle başlatıp bir sayfanın iki profilde aynı durum kodunu verdiğini ölçmek'
    - 'İşçi sürecin belleğinde tutulan durumun istekler arasında taşınmadığını, dosyaya yazılan durumun taşındığını yorumlamak'
---

# Süreç Yöneticisi ve Web Sunucusu

İstekleri kimin karşıladığını ve işçi süreçlerin istekler arasında neyi paylaşmadığını yerleşik web sunucusuyla ölçmek: durumu süreç dışında tutmak neden gerekir.

Bir tarayıcı PHP uygulamasına istek gönderdiğinde araya iki katman girer. Web sunucusu (web server) bağlantıyı kabul eder ve isteği okur. Süreç yöneticisi (process manager) PHP kodunu çalıştıran işçi süreçleri (worker process) başlatır, izler ve istekleri aralarında dağıtır. Yönetici işçi sayısını artırıp azaltabilir, çöken bir işçinin yerine yenisini koyabilir. Ayrıntıların hepsi üretim kurulumuna bağlıdır; bu derste kurulum yerine bu işin küçük bir modeli olan yerleşik web sunucusu (built-in web server) ölçülür.

Yerleşik sunucu yalnız `php -S` ile koşar ve üretim için tasarlanmamıştır; birden çok işçi süreç desteği PHP 7.4.0 ile gelen deneysel bir özelliktir ve Windows'ta desteklenmez. Üretimde web sunucusu ve süreç yöneticisi ayrı bir yapılandırmayla kurulur. Yerleşik sunucu bu yapıyı ölçülebilir bir kapsama indirir: `PHP_CLI_SERVER_WORKERS` ortam değişkeni işçi süreç sayısını belirler ve her işçi ayrı bir süreçtir. Ölçü ekseni bu kursun ortak sorusudur: bu davranış üretime çıkıyor mu? `yan.php` birebir aşağıdadır, yalnız diske yazılır.

```php
<?php
// yan.php — bu davranış üretime çıkıyor mu?
declare(strict_types=1);

// PHP'nin dağıttığı iki ayar dosyası (php.ini-development, php.ini-production) yedi
// yönergede ayrışır; yedisi buraya birebir alındı. Üretimdeki son iki satır dağıtım
// dosyasında yorum satırıdır: kod önbelleği komut satırında da açık, dosya denetimi kapalı.
const PROFILLER = [
    'geliştirme' => [
        'display_errors' => '1',
        'display_startup_errors' => '1',
        'error_reporting' => '32767',
        'mysqlnd.collect_memory_statistics' => '1',
        'zend.assertions' => '1',
        'zend.exception_ignore_args' => '0',
        'zend.exception_string_param_max_len' => '15',
    ],
    'üretim' => [
        'display_errors' => '0',
        'display_startup_errors' => '0',
        'error_reporting' => '24575',
        'mysqlnd.collect_memory_statistics' => '0',
        'zend.assertions' => '-1',
        'zend.exception_ignore_args' => '1',
        'zend.exception_string_param_max_len' => '0',
        'opcache.enable_cli' => '1',
        'opcache.validate_timestamps' => '0',
    ],
];

/** Betiği ayar dosyası olmadan, yalnız profilin yönergeleriyle ayrı bir süreçte koşturur. */
function calistir(string $betik, array $yonergeler): bool {
    $komut = ['php', '-n'];
    foreach ($yonergeler as $ad => $deger) {
        array_push($komut, '-d', "$ad=$deger");
    }
    $komut[] = $betik;
    $surec = proc_open($komut, [1 => ['pipe', 'w'], 2 => ['file', '/dev/null', 'w']], $borular);
    $cikti = stream_get_contents($borular[1]);
    fclose($borular[1]);
    proc_close($surec);
    $satirlar = array_values(array_filter(array_map('trim', explode("\n", $cikti))));
    return end($satirlar) === 'evet';
}

/** Davranışın iki koşumda görülüp görülmediğinden yanını çıkarır. */
function yan(bool $gelistirmede, bool $uretimde): string {
    return match (true) {
        $gelistirmede && $uretimde => 'iki yanda da',
        $gelistirmede => 'yalnız geliştirmede',
        $uretimde => 'yalnız üretimde',
        default => 'koşuma girmiyor',
    };
}

function yanRaporu(string $ad, string $betik): string {
    $gelistirmede = calistir($betik, PROFILLER['geliştirme']);
    $uretimde = calistir($betik, PROFILLER['üretim']);
    $sonYan = yan($gelistirmede, $uretimde);
    echo "$ad → $sonYan\n    geliştirme ", $gelistirmede ? 'evet' : 'hayır',
        ' · üretim ', $uretimde ? 'evet' : 'hayır', "\n";
    return $sonYan;
}
```

Önce sunucuyu iki profilde başlatan betiği yazalım. Sunucu her zaman `-d display_errors=0` ile ve yalnız `127.0.0.1` adresinde açılır. Profil yönergeleri `yan.php` içindeki `PROFILLER` tablosundan alınmıştır; tek bilinçli ayrım `display_errors` yönergesidir: bu derste yanıt gövdesi değil günlük ölçüldüğü için iki profilde de `0` verilir, tablodaki geliştirme değeri `1` burada kullanılmaz. İki profil de aynı işçi sayısıyla başlar. Betik iki sayfa yazar: `sayac.php` her istekte bellekteki bir sayacı artırıp yazdırır, `uyari.php` tanımsız bir değişken okuyarak uyarı üretir. Sunucu hazır olana kadar `curl` ile beklenir, sonunda `kill` ile durdurulur. Betik sonuçları `sunucu-sonuc.txt` dosyasına da yazar.

```bash
# sunucu.sh — yerleşik sunucuyu iki işçiyle iki profilde başlatır
cat > sayac.php <<'EOF'
<?php
$GLOBALS['sayac'] = ($GLOBALS['sayac'] ?? 0) + 1;
echo $GLOBALS['sayac'];
EOF
cat > uyari.php <<'EOF'
<?php
echo $tanimsiz;
echo 'tamam';
EOF

GELISTIRME="-d display_startup_errors=1 -d error_reporting=32767 -d mysqlnd.collect_memory_statistics=1 -d zend.assertions=1 -d zend.exception_ignore_args=0 -d zend.exception_string_param_max_len=15"
URETIM="-d display_startup_errors=0 -d error_reporting=24575 -d mysqlnd.collect_memory_statistics=0 -d zend.assertions=-1 -d zend.exception_ignore_args=1 -d zend.exception_string_param_max_len=0 -d opcache.enable_cli=1 -d opcache.validate_timestamps=0"
: > sunucu-sonuc.txt

for profil in gelistirme uretim; do
  if [ "$profil" = gelistirme ]; then yonergeler=$GELISTIRME; else yonergeler=$URETIM; fi
  rm -f gunluk-$profil.txt
  PHP_CLI_SERVER_WORKERS=2 php -n -d display_errors=0 -d log_errors=1 -d error_log=gunluk-$profil.txt \
    $yonergeler -S 127.0.0.1:8471 >/dev/null 2>&1 &
  pid=$!
  for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8471/sayac.php && break; sleep 0.1; done
  ilk=$(curl -s http://127.0.0.1:8471/sayac.php)
  ikinci=$(curl -s http://127.0.0.1:8471/sayac.php)
  ucuncu=$(curl -s http://127.0.0.1:8471/sayac.php)
  durum=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8471/uyari.php)
  kill $pid; wait $pid 2>/dev/null
  uyarilar=$(grep -c Warning gunluk-$profil.txt)
  echo "$profil $ilk $ikinci $ucuncu $durum $uyarilar" >> sunucu-sonuc.txt
  echo "$profil: sayaç $ilk $ikinci $ucuncu · uyarılı sayfa $durum · günlükte uyarı $uyarilar"
done
```

```
gelistirme: sayaç 1 1 1 · uyarılı sayfa 200 · günlükte uyarı 1
uretim: sayaç 1 1 1 · uyarılı sayfa 200 · günlükte uyarı 1
```

Şimdi iki profilin sonucunu okuyup yanını çıkaralım. Üç ölçüm sorusu var: sayaç her istekte `1` mi, uyarılı sayfa `200` mü, uyarı günlüğe düştü mü? Betik her birinin iki profilde aynı olup olmadığına bakar ve `yan.php` içindeki `yan()` fonksiyonuyla adlandırır.

```php
<?php
// sunucu-yan.php — sunucu ölçümlerinin yanını çıkarır
declare(strict_types=1);
require __DIR__ . '/yan.php';

$satirlar = array_map(
    fn(string $satir): array => explode(' ', trim($satir)),
    file('sunucu-sonuc.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES),
);
[$gelistirme, $uretim] = $satirlar;

$olcumler = [
    'sayaç her istekte 1' => fn(array $s): bool => array_slice($s, 1, 3) === ['1', '1', '1'],
    'uyarılı sayfa 200 verir' => fn(array $s): bool => $s[4] === '200',
    'uyarı günlüğe düşer' => fn(array $s): bool => (int) $s[5] === 1,
];

foreach ($olcumler as $ad => $olcum) {
    echo $ad, ' → ', yan($olcum($gelistirme), $olcum($uretim)), "\n";
}
```

```
sayaç her istekte 1 → iki yanda da
uyarılı sayfa 200 verir → iki yanda da
uyarı günlüğe düşer → iki yanda da
```

Sayaç iki profilde de her istekte `1` çıktı; iki işçi süreç ayrı bellek taşıdığı için bellekteki sayaç istekler arasında birikmedi. Uyarılı sayfa iki profilde `200` döndü ve uyarı ekrana değil günlüğe yazıldı. Bu yüzden üretimde bir sayfa uyarı verdiği hâlde yanıt sağlıklı görünür: durum kodu uyarıyı haber vermez, onu görmenin yolu günlüktür. (**BA4.** Her profil sunucuyu kendi ayarlarıyla baştan başlattı; çalışan bir sunucunun ayarını değiştirmek ölçülmedi.)

## İstek Başına Durum

Bir önceki ölçüm bellekteki sayacın istekler arasında birikmediğini gösterdi. Bu bölüm aynı soruyu dört farklı durum türü için sorar. PHP'de durumun yaşayabileceği yerler bellidir: bir sınıfın statik özelliği, global değişken, fonksiyon içindeki `static` değişken ve bir dosyanın `include_once` ile yüklenmesinin yan sonucu. Dört yerin her biri bir sayaç tutar ve sayfa dördünü birden yazdırır. Beklentimiz hepsinin `1` çıkmasıdır, çünkü PHP her istekte yürütme ortamını baştan kurar. Üç istek gönderilir ve yalnız üçüncünün yanıtı yazdırılır: birikme olsaydı sayılar orada `3` görünürdü.

```bash
# durum.sh — istek başına hangi durum taşınır?
cat > yuklu.php <<'EOF'
<?php
$GLOBALS['yuklu'] = ($GLOBALS['yuklu'] ?? 0) + 1;
EOF
cat > durum.php <<'EOF'
<?php
final class Tutucu {
    public static int $n = 0;
}
function say(): int {
    static $c = 0;
    return ++$c;
}
Tutucu::$n++;
$GLOBALS['g'] = ($GLOBALS['g'] ?? 0) + 1;
include_once __DIR__ . '/yuklu.php';
echo 'statik özellik ', Tutucu::$n, ' · global ', $GLOBALS['g'],
    ' · fonksiyon içi static ', say(), ' · include_once ', $GLOBALS['yuklu'];
EOF

GELISTIRME="-d display_startup_errors=1 -d error_reporting=32767 -d mysqlnd.collect_memory_statistics=1 -d zend.assertions=1 -d zend.exception_ignore_args=0 -d zend.exception_string_param_max_len=15"
URETIM="-d display_startup_errors=0 -d error_reporting=24575 -d mysqlnd.collect_memory_statistics=0 -d zend.assertions=-1 -d zend.exception_ignore_args=1 -d zend.exception_string_param_max_len=0 -d opcache.enable_cli=1 -d opcache.validate_timestamps=0"

for profil in gelistirme uretim; do
  if [ "$profil" = gelistirme ]; then yonergeler=$GELISTIRME; else yonergeler=$URETIM; fi
  PHP_CLI_SERVER_WORKERS=2 php -n -d display_errors=0 $yonergeler -S 127.0.0.1:8471 >/dev/null 2>&1 &
  pid=$!
  for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8471/durum.php && break; sleep 0.1; done
  curl -s -o /dev/null http://127.0.0.1:8471/durum.php
  curl -s -o /dev/null http://127.0.0.1:8471/durum.php
  son=$(curl -s http://127.0.0.1:8471/durum.php)
  kill $pid; wait $pid 2>/dev/null
  echo "$profil: $son"
done
```

```
gelistirme: statik özellik 1 · global 1 · fonksiyon içi static 1 · include_once 1
uretim: statik özellik 1 · global 1 · fonksiyon içi static 1 · include_once 1
```

Betiğin kurduğu düzeni okuyalım. `durum.php` dört ayrı yerde bir sayaç artırır. Birincisi `Tutucu` sınıfının statik özelliğidir; statik özellik sınıfa aittir, nesneye değil, ve bu yüzden "süreç boyunca yaşar" diye düşünülebilir. İkincisi global dizideki `g` anahtarıdır. Üçüncüsü `say()` fonksiyonunun içindeki `static $c` değişkenidir; bu değişken fonksiyon çağrıları arasında değerini korur. Dördüncüsü `yuklu.php` dosyasıdır: `include_once` dosyayı bir kez yükler ve dosyanın içindeki kod bir global sayaç artırır. `include_once` adındaki "bir kez", bir istek içinde bir kez demektir; sorumuz yeni bir istekte dosyanın yine yüklenip yüklenmediğidir.

Çıktının iki satırı da dört sayının hepsini `1` gösterir. Bu, PHP'nin istek modelinin ölçülmüş hâlidir: her istek, işçi süreç aynı kalsa bile, kullanıcı koduna göre sıfırdan kurulur. Statik özellik sıfırlanır, çünkü sınıf tanımı da değerleriyle birlikte her istekte yeniden oluşturulur. Global dizi istek bitince boşalır. Fonksiyon içi `static` değişken, fonksiyon her istekte yeniden tanımlandığı için sıfırdan başlar. `include_once` dosyayı her istekte yeniden yükler ve içindeki sayacı sıfırdan artırır. Dördü de aynı kuralın sonucudur ve iki profilde aynı olması, kuralın ayarlara bağlı olmadığını gösterir; bu yüzden bölümün sınıfı `iki yanda da`dır.

Bu sonucun gerçek üretimde iki yönü vardır. Olumlu yönü, bir isteğin bıraktığı kirin sonrakini bozmamasıdır: sızıntı yapan bir istek, bir sonraki isteğin değişkenlerini etkilemez. Bu özelliği ucuza veren model, bedelini başka yerden alır: her istek uygulamayı baştan kurar. Web uygulama çerçevesinin yapılandırmayı okuması, sınıfların yüklenmesi, bağlantıların açılması her istekte yinelenir. Kod önbelleği bu bedelin yalnız bir parçasını, dosyaları çevirme işini kaldırır. Kalan parça, yani nesnelerin kurulması, istek başına durumun doğal sonucudur. Uzun ömürlü çalışma modelleri bu bedeli azaltmak için durumu istekler arasında tutar ve bunun karşılığında sızıntı riskini kendileri yönetmek zorunda kalır. Bu derste o modeller kurulmadı; ölçülen yalnız işçi süreç başına bir isteğin klasik modelidir.


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

Bellekteki durum istekler arasında taşınmıyorsa durumu dosyaya yazmak çözüm olur mu? Aşağıdaki betik sayacı bir dosyada tutan sayfayı iki profilde başlatır ve üç isteğin döndürdüğü değerleri yazdırır.

```bash
# sinir.sh — durum dosyaya yazılırsa istekler arasında taşınır mı?
cat > dosyali.php <<'EOF'
<?php
$yol = __DIR__ . '/sayac.txt';
$n = (int) @file_get_contents($yol) + 1;
file_put_contents($yol, (string) $n);
echo $n;
EOF

GELISTIRME="-d display_startup_errors=1 -d error_reporting=32767 -d mysqlnd.collect_memory_statistics=1 -d zend.assertions=1 -d zend.exception_ignore_args=0 -d zend.exception_string_param_max_len=15"
URETIM="-d display_startup_errors=0 -d error_reporting=24575 -d mysqlnd.collect_memory_statistics=0 -d zend.assertions=-1 -d zend.exception_ignore_args=1 -d zend.exception_string_param_max_len=0 -d opcache.enable_cli=1 -d opcache.validate_timestamps=0"

for profil in gelistirme uretim; do
  if [ "$profil" = gelistirme ]; then yonergeler=$GELISTIRME; else yonergeler=$URETIM; fi
  rm -f sayac.txt
  PHP_CLI_SERVER_WORKERS=2 php -n -d display_errors=0 $yonergeler -S 127.0.0.1:8472 >/dev/null 2>&1 &
  pid=$!
  for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8472/dosyali.php && break; sleep 0.1; done
  rm -f sayac.txt
  ilk=$(curl -s http://127.0.0.1:8472/dosyali.php)
  ikinci=$(curl -s http://127.0.0.1:8472/dosyali.php)
  ucuncu=$(curl -s http://127.0.0.1:8472/dosyali.php)
  kill $pid; wait $pid 2>/dev/null
  echo "$profil: dosyadaki sayaç $ilk $ikinci $ucuncu"
done
```

```
gelistirme: dosyadaki sayaç 1 2 3
uretim: dosyadaki sayaç 1 2 3
```

Dosyadaki sayaç iki profilde de `1 2 3` çıktı: iki işçi aynı dosyayı gördü ve istekler arasında durum taşındı. Bellekteki sayaç ise `1 1 1` kalmıştı. Fark işçinin belleğinde değil, durumun nerede tutulduğundadır. İşçi süreçlere bağlı değişkenler, statik özellikler ve önbelleğe alınmış nesneler istekler arasında güvenilir biçimde paylaşılmaz; paylaşılması gereken durum süreç dışında, bir dosyada, veritabanında ya da bir önbellek sunucusunda tutulur. Burada dosyayı kullandık, çünkü ölçüm için en küçük dış depodur; sıralı üç istek çakışmaz. Aynı anda yazan iki işçi için dosya kilidi ya da gerçek bir depo gerekir, bu da ölçülmedi.

Bu ölçümler birlikte işçi sayısı kararına ışık tutar. Her işçi süreç, kendi istek belleğiyle çalışır; iki işçi, aynı anda iki isteği karşılayabilir ama birbirlerinin belleğini görmez. Bu yüzden işçi süreç sayısını artırmak, aynı anda karşılanabilecek istek sayısını artırır ve toplam bellek kullanımını da artırır. Bir işçi sürecin tipik bellek tutarını bilmeden sayıyı artırmak, makinenin belleğini aşıp sistemi yavaşlatmaya götürebilir. Eşzamanlılık sayıyla gelir, ama sayının üst sınırını bellek koyar. Dağıtım belgenize "iki işçi" yerine "işçi başına şu kadar bellek, toplamda şu kadar işçi" yazmak, kararı sonradan açıklanabilir kılar.

(**BA6.** Sunucunun profil yönergeleri `yan.php` içindeki `PROFILLER` tablosuyla aynıdır, yalnız `display_errors` iki profilde de `0` verilir; tablodaki geliştirme değeri `1`dir. Bu derste yanıt gövdesi değil yalnız günlük ve durum kodu ölçüldü; `display_errors` ayrımının yanıta etkisi ölçülmedi, sonraki yapılandırma ayarı dersinde ele alınır.) Yerleşik sunucunun işçi süreçleri ölçüm için seçildi. Üretimde süreç yöneticisinin işçi sayısını nasıl belirlediği, işçilerin kaç istekten sonra yeniden başlatıldığı ve bir işçi çöktüğünde ne olduğu yapılandırma konularıdır ve bu ölçümün kapsamında değildir. Ölçüm, işçi sayısı iki iken bile iki profilin aynı durumu verdiğini ve durumun istekler arasında taşınmadığını gösterdi. Bu iki bilgi, sonraki derste yapılandırma farklarını konuşurken zemin olacak. Özetle: durum işçi süreçte değil, onun dışında yaşar; işçi süreç sayısı da bu yüzden eşzamanlılığı belleğe karşı tartan bir dağıtım kararıdır.

## Özet

- Web sunucusu bağlantıyı alır, süreç yöneticisi PHP kodunu çalıştıran işçi süreçleri başlatır, izler ve istekleri aralarında dağıtır.
- Yerleşik web sunucusu yalnız `php -S` ile koşar, üretim için değildir; bu derste iki işçili küçük bir model olarak kullanıldı.
- Bu dersin ölçü sınıfı **iki yanda da**dır: bellekteki sayaç iki profilde de her istekte `1` verdi, uyarılı sayfa iki profilde `200` döndü ve uyarı günlüğe düştü.
- Durum kodu uyarıyı haber vermez; üretimde uyarıyı görmenin yolu günlüktür.
- Bellekteki durum istekler arasında taşınmaz, dosyadaki taşınır: paylaşılacak durum süreç dışında tutulur.
- Statik özellik, global değişken, fonksiyon içi `static` değişken ve `include_once` sayaçlarının dördü de üçüncü istekte `1` verdi; istek başına durum iki profilde aynıdır (ölçü sınıfı: iki yanda da).

## Sonraki Adım

İşçi süreç ayarlarıyla başlar: bellek sınırı, hata raporlama ve kod önbelleği yönergeleri süreç başlarken verilir. Son ders iki profilin yedi yönergede nasıl ayrıştığını yazar ve çalışırken bir yönergeyi `ini_set()` ile değiştirmenin hangi profilde kabul edildiğini ölçer.
