php-fpm max_children uyarısı: Worker artırmadan önce nedeni kayıtlardan bulun

php-fpm max_children uyarısı, PHP worker süreçlerinin hepsinin istek işlediği ve yeni isteklerin kuyruğa girdiği anlamına gelir. Bu uyarının haftalardır biriktiği Nginx, Apache ve php-fpm yapılandırmalı bir WordPress sunucusunda worker'ları dolduran istekleri kayıtlardan ayıkladım ve ayar değerini yükseltmek yerine nginx hız sınırıyla çözdüm.
Haftalık kontrolde uyarı günde 27–187 kez kaydedildi, ama aynı dört günde ziyaretçilerin aldığı 502 ya da 504 hatası 0'dı. Worker'ları dolduran ziyaretçiler değil, yönetici adreslerini tekrar tekrar isteyen bir bot ve olmayan yolları isteyen bir tarayıcıydı.
1️⃣ php-fpm max_children & seems busy
Uyarı günde 66'ya ulaştığında bunu worker'ların 66 kez yetmediği şeklinde okumak kolaydır. php-fpm kaydında niteliği farklı iki uyarı karışıktır ve çoğu, sınıra ulaşılmadan önce kaydedilen ön uyarılardır.
[09-Oct-2026 00:16:18] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 15 total children
[09-Oct-2026 13:02:05] WARNING: [pool www] server reached pm.max_children setting (26), consider raising it[seems busy], boşta worker kalmadığı ve yeni süreç oluşturulduğu anlamına gelir; süreç yönetimi dynamic ise isteklerdeki kısa bir artışta bile kaydedilir. [server reached pm.max_children] ise 26 sınırına ulaşılıp daha fazla oluşturulamadığı andır; bundan sonra yeni istekler bekler.
Bu, lokantada bir garson daha çağırmakla bütün masalar dolu olduğu için müşterileri dışarıda bekletmek arasındaki farktır.
Bir günü türlerine göre sayınca 58 seems busy ve 6 max_children çıktı. Kontrol rutini ikisini toplayıp 66 olarak bildirdi, ama sınıra gerçekte altı kez ulaşıldı.
grep '09-Oct-2026' /var/log/php-fpm/error.log | grep 'pool www' | grep -c 'seems busy'58grep '09-Oct-2026' /var/log/php-fpm/error.log | grep 'max_children' | cut -c2-2109-Oct-2026 13:02:05
09-Oct-2026 13:52:10
09-Oct-2026 18:09:20
09-Oct-2026 18:42:17
09-Oct-2026 18:46:07
09-Oct-2026 19:12:44İkinci komutun yazdırdığı altı zaman, nedeni izlemenin dayanağı olur; sonraki adım yalnızca bu zamanları kullanır.
2️⃣ max_children artırmanın çözüm olmamasının nedeni
26 sınırını yükseltmek uyarı sayısını azaltabilir, ama worker'ları hangi isteklerin doldurduğunu değiştirmez.
Genel hesap, kullanılabilir belleği bir worker'ın ortalama belleğine bölmektir. Ölçülen sunucuda 26 worker ortalama 131MB, en fazla 214MB kullandı; kalan kullanılabilir bellekle hesaplanınca kâğıt üzerinde daha fazla worker oluşturmaya yer vardı.
ps -o rss= -u www -C php-fpm | awk '{s+=$1; n++; if($1>m) m=$1} END{print n, s/n/1024, m/1024, s/1024}'26 130.579 214.27 3395.05free -mİlk çıktıdaki dört değer worker sayısı, ortalama bellek (MB), en yüksek bellek (MB) ve toplamdır (MB); free çıktısındaki available sütunu kullanılabilir bellektir.
Worker sayısı CPU çekirdek sayısını geçince worker artırmak aynı anda işlenen istekleri artırmaz, yalnızca her isteğin süresini uzatır. Ziyaretçilerin 502 ya da 504 hatası 0 iken sınırı yükseltmek bir şey kazandırmaz; worker'ları tutan botlara ve tarayıcılara daha fazla süreç verir.
Sınırı yükseltmeden önce uyarı zamanlarında worker'ları tutan isteklerin ziyaretçiden gelip gelmediğini kontrol edin; bunu access.log ile nasıl kontrol edeceğinizi sonraki bölümde derledim.
3️⃣ access.log ile nedeni izleme: Zaman, IP, yol, yanıt kodu
Bir uyarı zamanı seçin; yanıt o 1–2 dakikanın access.log'undadır. Sıra şöyledir: satırları zamana göre ayıklayın, IP'ye göre sayın, sonra en üstteki IP'lerin yollarına ve yanıt kodlarına bakın.
grep '09/Oct/2026:18:09' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head 115 140.245.x.x
61 187.15.x.x
17 3.36.x.x
8 91.239.x.x
4 79.127.x.x
3 109.248.x.x
3 109.248.x.x
3 109.248.x.x
2 79.127.x.x
2 40.77.x.xgrep '09/Oct/2026:18:09' /var/log/nginx/access.log | awk '{print $7, $9}' | sort | uniq -c | sort -rn | head 55 / 200
13 /wp-login.php 404
13 /wp-admin/plugins.php 302
13 /wp-admin/index.php 302
13 /wp-admin/edit.php 302
12 /wp-admin/profile.php 302
4 / 499
3 /xmlrpc.php 302
2 /xmlrpc.php 499
2 /wp-content/themes/kinsta/images/duos/small/wheelchair.svg 4049 Ekim saat 18.09'da bir dakikada 286 istek kaydedildi; bunların 115'i tek bir IP'dendi, yollar /wp-admin/profile.php gibi yönetici adresleriydi ve yanıt kodlarının hepsi 302'ydi.
Yanıt kodu PHP'ye ulaşılıp ulaşılmadığını gösterir. nginx'in doğrudan verdiği önbellekli HTML için 200 ile 444 ve 400 PHP'ye hiç uğramaz; 302, 404 ve önbelleksiz 200 ise her biri bir worker kullanır. Oturum açmamış biri yönetici adresi isteyince WordPress giriş sayfasına 302 ile yanıt verir ve o tek 302, bir PHP çalıştırmasıdır.
Bir sunucuda birden fazla site varsa genel access.log iki sitenin satırlarını birlikte kaydeder. İlk başta bu botun ilk siteye saldırdığını düşündüm, ama site başına kayıtlarla karşılaştırınca 22.391 isteğin 22.390'ının ikinci siteye ait olduğu görüldü.
4️⃣ Worker'ları dolduran üç tür istek: Botun wp-admin'i, tarayıcının 404'ü, önbellek ön yüklemesi
Aynı sırayı uyarının kaydedildiği her zamana uygulayınca worker'ları dolduran isteklerin üç türde olduğu görüldü.
Birincisi yönetici adreslerini tekrar tekrar isteyen bir bottu. Tek bir IP üç saatte 22.391 istek gönderdi; bunların 9.944'ü /wp-admin/profile.php dahil dört yola gitti ve oturum açmamış her 302 bir PHP çalıştırması olduğu için 9.944 kez worker kullandı.
İkincisi olmayan yolları isteyen bir güvenlik açığı tarayıcısıydı. User-Agent'ta Googlebot yazsa da Googlebot değildir: günde 14.672 istek gönderen IP, ters DNS'i googleusercontent.com ile biten bir Google Cloud sanal sunucusuydu; Googlebot ise googlebot.com ile biter. Aynı IP bingbot, Applebot, GPTBot ve ClaudeBot adlarını sırayla kullandı.
.env gibi dosya isteklerini nginx 0,04 saniyede 444 ile keser, ama /userfiles ya da /secrets.yml gibi engelleme kurallarında olmayan sıradan yollar WordPress'e 404 sayfası oluşturtur. Bu 404 sayfası 266KB ve 1,4–2,9 saniye tutar, yani normal bir yazı oluşturmakla aynı maliyettedir; tek bir IP onu günde 4.069 kez istedi.
Üçüncüsü sunucunun kendisiydi. Bir yazı ya da çeviri kaydedilince önbellek boşalır ve WP Rocket ön yüklemesi tüm adresleri yeniden ister; bu günde 8.489 istek, dakikada en fazla 60 istek ediyordu. Rank Math'in dizin durumu kontrolleri de Action Scheduler üzerinden günde 754 kez bir worker kullandı. İkisi de çalışma saatlerinde yoğunlaştı ve ziyaretçi hatasına yol açmadı; bu yüzden onları düzeltmenin dışında bıraktım.
Aşağıdaki simülatörde her isteğin hangi katmanda bittiğini ve PHP worker kullanıp kullanmadığını görebilirsiniz.
| İstek | Durduğu katman | Yanıt | İşçiler |
|---|---|---|---|
| Botun wp-admin'i | WordPress | 302 (giriş sayfasına) | 1 kullanır |
| Tarayıcı .env türü | nginx engelleme kuralı | 444 (0,04 sn) | Kullanılmıyor |
| Tarayıcı sıradan sahte yol | WordPress | 404 sayfası (266KB · 1,4–2,9 sn) | 1 kullanır |
| Önbellek ön yüklemesi | WordPress | 200 · önbellek dosyası olarak kaydedilir | 1 kullanır |
| Önbellekli yazı ziyareti | Önbellek dosyası | 200 (nginx doğrudan yanıt verir) | Kullanılmıyor |
| Önbelleksiz yazı ziyareti | WordPress | 200 · önbellek dosyası olarak kaydedilir | 1 kullanır |
5️⃣ nginx hız sınırı: Oturum açmamış istekleri sınırlama ve gerçek IP'yi geri getirme
Engellemek yerine hız sınırını seçtim. Yolu engellerseniz tarayıcı yol değiştirir, IP'yi engellerseniz ertesi gün başka bir IP gelir. Yalnızca PHP'ye ulaşan oturum açmamış istekler IP başına dakikada 30 ve anlık 60 ile sınırlanır, fazlası 429 alır.
CDN arkasındaki bir sunucu için bu ayar tek başına yetmez. nginx'in gördüğü IP CDN uç adresidir; o uçtan geçen tüm ziyaretçiler tek IP'de toplanır. Önce uç IP listesini set_real_ip_from ile kaydedip X-Forwarded-For'daki gerçek IP'yi geri getirmeniz gerekir. Oturum açma çerezi olan istekler muaf tutulur, ama User-Agent'a göre muafiyet yoktur.
# http bağlamı
real_ip_header X-Forwarded-For;
real_ip_recursive on;
set_real_ip_from 203.0.113.0/24; # CDN uç listesi
map $http_cookie $php_anon_key {
default $binary_remote_addr;
~*wordpress_logged_in '';
}
limit_req_zone $php_anon_key zone=php_anon:10m rate=30r/m;
# PHP'ye gönderen location
location @apache {
limit_req zone=php_anon burst=60 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8080;
}Önbellekli sayfalar nginx içinde try_files ile yanıtlandığından bu location'a gelmez; admin-ajax.php ayrı bir location olduğu için sınıra dahil değildir.
Doğrulamayı eşzamanlı isteklerle yapın. Sıralı istekler bir 404 1,5 saniye sürdüğü için dakikada 30'u geçemez ve sınıra takılmaz; 20'şer eşzamanlı göndererek 100 istek yapınca 67 kez 404 ve 33 kez 429 alınır ve sınırın çalıştığı doğrulanır.
seq 1 100 | xargs -P 20 -I{} curl -s -o /dev/null -w '%{http_code}\n' https://example.com/zz-{} | sort | uniq -cÇıktıda 429 sayılıyorsa sınır çalışıyordur. Sunucunun kendi IP'sini muaf tuttuysanız test sırasında muafiyeti kısa süre kaldırın ya da başka bir ağdan çalıştırın.
6️⃣ Önce ve sonra
Oturum açmamış yönetici adresi sınırı 9 Ekim akşamı, PHP'ye ulaşan istek sınırı ise 11'i akşamı devreye girdi. Yönetici adresi 302'leri 9'unda 9.996'dan 10'unda 11'e, 11'inde 1'e düştü; üç saatte 22.391 istek gönderen IP ertesi günden beri hiç istek göndermedi.
Tarayıcı tarafında sınır yeni uygulandığı için henüz tam günlük rakam yok; karar, WordPress'in oluşturduğu 404 sayfası sayısının (9'unda 2.563, 10'unda 2.612, 11'inde 1.630) gelecek haftaki kontrolde ne kadar düştüğüne göre verilecek. Ziyaretçilerin 502 ve 504 hataları öncesinde de sonrasında da 0'dı.
Bu rakamlar Nginx, Apache ve php-fpm ile çalışan bir WordPress sunucusunun kayıtlarından doğrudan sayıldı; karar uyarı sayısına değil, ziyaretçi hatalarına ve PHP'ye ulaşan istek sayısına dayandı.
🔢 SSS & önerilen içerikler
Önbellek
Web sitesi önbelleği & sunucu içi önbellek
Önbellekli isteklerin PHP'ye uğramadığı yapıyı katman katman açıklar.
Güvenlik
wp-config.php güvenlik açıkları
Tarayıcıların hedef aldığı ayar dosyasını izinler ve sunucu kurallarıyla koruma yöntemi.
Linux
15 kategoride Linux komutları
Kayıtları saymakta kullanılan grep ve awk dahil komutları 15 kategoriye ayıran tablo.
ℹ️ Satış ortaklığı bildirimi
Bu sitenin içeriklerinde satış ortaklığı bağlantıları bulunur. Bir ziyaretçi bu bağlantılar üzerinden ürün veya hizmet satın alırsa bu site satıcıdan komisyon alır. Bu süreçte alıcının ödediği tutar (kampanya indirimlerinde tutar düşer↓) artmaz. Yayımlanan fiyat, indirim ve stok bilgileri yazının yazıldığı zamana aittir ve gerçek durumdan farklı olabilir; bu yüzden satın almadan önce son bilgiyi satıcıdan kontrol etmenizi rica ederiz. Ürünler kendi ölçütlerimize göre seçilir ve değerlendirilir; komisyon alınıp alınmaması tanıtım sırasını veya değerlendirme içeriğini etkilemez.