Diyaliz ve yoğun bakım servislerinde hekimlerin en çok dikkat ettiği mekanizmalardan biri "Ekstrakorporeal Dolaşım"dır (Vücut dışı kan devridaimi). Kronik böbrek yetmezliği olan hastanın kanı damardan alınır, steril hortumlarla diyaliz makinesine gönderilir, filtre edilir ve tekrar hastanın bedenine pompalanır. Bu sistemde hayati emniyet sensörleri vardır: Eğer diyaliz makinesinin pompası durursa veya filtre tıkanırsa, makine saniyenin onda birinde klempleri sıkar, kanı hastanın vücuduna geri yönlendirir ve alarm çalar.
Şimdi düşünün ki o makinede hiçbir zaman aşımı (timeout) veya emniyet subapı yoktur. Dışarıdaki makinenin elektriği kesilir, pompa durur. Hastanın tüm kanı o harici makinenin içinde rehin kalır; kalbe kan dönemez, kan pıhtılaşır ve hasta masanın üzerinde hipovolemik şoktan hayatını kaybeder! Hastanın kendi organları sapasağlamdır; fakat dışarıdaki yabancı bir cihazın arızası, hastanın tüm dolaşım sistemini bir anda durdurmuştur.
Yazılım mimarisinde ve PHP geliştirmede acemi geliştiricilerin yazdığı ve en güçlü sunucuları bile saniyeler içinde komaya sokan o ölümcül mimari basiretsizlik tam olarak bu emniyetsiz diyaliz hortumudur: Zaman aşımı (timeout) kontrolü olmadan, senkron bir şekilde file_get_contents() ile dışarıdan veri çekmeye çalışmak.
"Ana Sayfaya Canlı Dolar Kuru Koydum!"
Bir haber portalı, kurumsal şirket veya e-ticaret sitesi... Sitenin üst bilgi (header) kısmında ziyaretçilere güncel hava durumunu veya canlı dolar/euro kurunu göstermek istemektedirler.
Geliştirici açar header.php dosyasını. Kodu en kestirme, en ilkel yoldan yazar:
<?php
// Tembel yazılımcının saatli bombası:
$doviz_json = file_get_contents("http://api.harici-doviz-sitesi.com/canli-kurlar.json");
$kurlar = json_decode($doviz_json, true);
echo "Dolar: " . $kurlar['USD'];
?>Geliştirici testi yapar. Sayfa açılır, dolar kuru pırıl pırıl görünmektedir. Geliştirici kodu canlıya alır ve evine gider.
İki ay boyunca her şey harika çalışır. Ta ki o kara salı gününe kadar...
Karşı Tarafın Çöküşü ve Zincirleme Reaksiyon
Salı günü saat 14:00'te, verinin çekildiği o üçüncü parti harici-doviz-sitesi.com sunucusunda bir arıza yaşanır veya aşırı yükten dolayı sunucuları yavaşlar. Normalde 50 milisaniyede yanıt veren o API, artık istek başına 60 saniyede yanıt veremez hale gelir!
Ve felaket zinciri başlar:
- İlk ziyaretçi sitenize girer.
header.phpçalışır.file_get_contents()fonksiyonu harici sunucuya bağlanır ve TCP soketini açar. Karşı taraf yanıt vermediği için PHP motoru o satırda beklemeye (blocking I/O) başlar. - İkinci ziyaretçi gelir; ikinci bir PHP worker'ı ayağa kalkar, o da döviz API'sini beklemeye başlar.
- Beşinci, onuncu, ellinci ziyaretçi...
- 30 saniye içinde sunucunun PHP-FPM havuzundaki tüm worker'lar (Örn: 50 adet worker), o yabancı sitenin kapısında kuyruğa girmiş, eli kolu bağlı bir şekilde donakalmıştır!
Sunucuda başka hiçbir ziyaretçinin isteğini karşılayacak tek bir boş PHP iş parçacığı kalmaz!
Web sunucusu (Nginx/Apache), PHP motoruna "Şu sayfayı derle" der; PHP'den yanıt alamaz. Ve ziyaretçilerin ekranına o buz gibi hata basılır:
504 Gateway Timeout
The gateway did not receive a timely response from the upstream server or application.
Site tamamen çökmüştür! Şirket genel müdürü bilgi işlem müdürünü arar: "Milyonluk sunucu aldık, sitemiz açılmıyor, 504 veriyor!"
Mühendis sunucuya bakar: İşlemci kullanımı %1, bellek kullanımı %5'tir! Sunucu donanımsal olarak uyumaktadır; fakat tüm PHP iş parçacıkları dışarıdaki o dandik döviz sitesinin keyfini beklemekten kilitlenmiştir!
file_get_contents(): Emniyet Kemeri Olmayan Araç
Varsayılan haliyle PHP'nin file_get_contents() fonksiyonu harici HTTP istekleri için tasarlanmamıştır; yerel diskten dosya okumak için üretilmiştir.
Bir URL verdiğinizde file_get_contents():
- Zaman aşımı (timeout) tanımaz: Karşı taraf 5 dakika yanıt vermezse, PHP de 5 dakika boyunca o satırda donup kalır!
- Hata yakalayamaz (No Graceful Degradation): Karşı taraf 500 hatası dönerse doğrudan PHP Fatal/Warning patlatır.
- Senkron ve Engelleyicidir (Blocking): O veri gelmeden sayfanın geri kalan HTML'i ziyaretçiye asla teslim edilmez.
Yani siz kendi web sitenizin şahdamarını, internetteki tanımadığınız üçüncü bir şahsın sunucusunun sıhhatine bağlamış olursunuz! Karşı taraf hapşırsa, sizin siteniz yoğun bakıma düşer.
Cerrahi Güvence: Zaman Aşımı, cURL ve Önbellekleme
Harici servislerden veri çekerken sisteminizin asla kilitlenmemesini sağlamanın üç altın mühendislik kuralı vardır:
1. Asla Sayfa Yüklenirken Canlı Veri Çekmeyin (Cron + Cache)!
Bir ziyaretçi sitenize girdiğinde canlı canlı dışarıdan döviz kuru çekmek mimari bir cinayettir!
- Sunucunuza arka planda saatte bir veya 10 dakikada bir çalışan bağımsız bir Cron Job kurun.
- Bu cron görevi gitsin döviz kurunu çeksin ve yerel bir Redis önbelleğine veya veritabanına yazsın.
- Ziyaretçi sitenizi açtığında harici siteye hiç gitmesin; veriyi yerel RAM'den (Redis) 0.001 milisaniyede okusun! Dışarıdaki döviz sitesi 3 gün boyunca çökse bile sizin siteniz yerel önbellekteki son veriyi gösterir ve bir milisaniye bile yavaşlamaz!
2. cURL ile Katı Zaman Aşımı (Timeout) Tanımlayın
Eğer mutlaka canlı bir istek atılacaksa, file_get_contents yerine cURL kullanın ve zaman aşımını en fazla 2-3 saniye ile sınırlandırın:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://api.harici-site.com/kur.json");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); // Bağlantı için en fazla 2 saniye bekle!
curl_setopt($ch, CURLOPT_TIMEOUT, 3); // Yanıt için en fazla 3 saniye bekle!
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200 || !$response) {
// Karşı taraf çöktüyse panik yok, varsayılan/eski veriyi göster:
$kurlar = ['USD' => '34.50 (Tahmini)'];
}3. İstemci Taraflı Yükleme (Asenkron AJAX / Fetch)
Döviz kurunu veya hava durumunu PHP ile sunucu tarafında çekmek yerine; sayfa yüklendikten sonra kullanıcının tarayıcısına JavaScript (fetch()) ile çektirin. Karşı taraf yavaşlasa bile kullanıcının tarayıcısında sadece o küçük kutucuk döner; sitenizin ana gövdesi ışık hızında açılır.
Harici makine durduğunda hastanın kanını dışarıda unutan sağlıkçı nasıl hastayı öldürürse; zamanaşımsız kod yazan yazılımcı da kendi altyapısını başkalarının hatalarına kurban eder. Profesyonel mimari, dış dünyanın her an çökebileceği paranoyasıyla yazılan; kendi kalesini asla yabancıların insafına terk etmeyen akılcı bir savunma sanatıdır.