- Konu sahibi
- #1
PHP’de varsayılan dosya tabanlı oturum işleyicisi kullanılıyorsa aynı oturum kimliğine sahip istekler,
İstek
Bu kilit kullanıcılar arasında genel bir PHP kilidi değildir. Farklı oturum kimlikleri kullanan iki kullanıcının istekleri normalde birbirini bu nedenle bekletmez. Sorun, aynı oturumu paylaşan paralel isteklerde görülür. Ayrıca özel bir session handler kullanılıyorsa kilitleme davranışı handler’ın uygulamasına bağlıdır; dosya handler’ındaki davranış otomatik olarak diğer depolara genellenmemelidir.
Kilidin amacı, iki isteğin aynı oturum verisini aynı anda okuyup yazarken birbirinin değişikliklerini ezmesini önlemektir. Ancak kilit açıkken yapılan ağ çağrısı, dosya işlemi, yoğun hesaplama veya uzun
Aşağıdaki dosya, aynı oturumla yapılan ilk isteği birkaç saniye bekletir.
Ayrı ve boş bir yerel deneme dizininde dosyayı lock.php olarak kaydedin. Aşağıdaki komut ve curl akışı POSIX kabuğu bulunan Linux/macOS içindir. Varsayılan php -S tek işçiyle çalışır; istekleri zaten sıraya aldığı için session kilidi deneyini tek başına kanıtlayamaz. PHP’nin desteklenen geliştirme ortamında en az iki işçiyle, yalnız loopback üzerinde başlatın:
Bu çok işçili CLI seçeneği Windows’ta desteklenmez. Windows’ta eşzamanlı istek işleyebilen ayrı bir test web sunucusu veya Linux geliştirme ortamı kullanın; tek işçili sunucudaki beklemeyi oturum kilidi sanmayın. Geliştirme sunucusunu genel ağa açmayın. Aynı oturum çerezini kullanarak önce oturumu oluşturun, ardından iki isteği başlatın:
Beklenen davranış, ikinci
İstek yalnızca oturum verisini okuyacaksa oturumu okuma sonrasında kapatmaya yönelik PHP seçenekleri kullanılabilir; fakat bu yaklaşımda oturum üzerinde yapılacak değişikliklerin kaydedilmeyeceği varsayılmalıdır. Yazma gereken yerlerde açıkça
session_start() sonrasında oturum verisi üzerinde kilit oluşturur. İlk istek kilidi bırakana kadar aynı kullanıcıya ait ikinci istek genellikle bekler. Bu nedenle uzun süren bir işlem oturum açıkken çalışıyorsa, kullanıcının diğer AJAX veya sayfa istekleri de yavaşlayabilir. Oturum verisine artık ihtiyacınız kalmadığında session_write_close() çağırmak kilidi erken bırakır.Oturum kilidi nasıl çalışır?
İstek
session_start() çağırdığında PHP oturum kaydını açar ve kullanılan handler’ın davranışına göre kilitler. Varsayılan dosya handler’ında bu kilit, oturum kapatılana veya istek sona erene kadar tutulur. Aynı PHPSESSID çerezini taşıyan başka bir istek aynı kaydı açmaya çalışırsa bekler.Bu kilit kullanıcılar arasında genel bir PHP kilidi değildir. Farklı oturum kimlikleri kullanan iki kullanıcının istekleri normalde birbirini bu nedenle bekletmez. Sorun, aynı oturumu paylaşan paralel isteklerde görülür. Ayrıca özel bir session handler kullanılıyorsa kilitleme davranışı handler’ın uygulamasına bağlıdır; dosya handler’ındaki davranış otomatik olarak diğer depolara genellenmemelidir.
Kilidin amacı, iki isteğin aynı oturum verisini aynı anda okuyup yazarken birbirinin değişikliklerini ezmesini önlemektir. Ancak kilit açıkken yapılan ağ çağrısı, dosya işlemi, yoğun hesaplama veya uzun
sleep() çağrısı gereksiz bekleme oluşturur.Somut örnek
Aşağıdaki dosya, aynı oturumla yapılan ilk isteği birkaç saniye bekletir.
session_write_close() çağrısı bu beklemeden sonra çalıştığı için ikinci istek, ilk istek kilidi bırakmadan yanıt üretemez.
PHP:
<?php
header('Content-Type: text/plain; charset=utf-8');
session_start();
$label = $_GET['label'] ?? 'normal';
if (!is_string($label)) $label = 'normal';
$label = substr($label, 0, 40);
$_SESSION['last_label'] = $label;
$early = ($_GET['early'] ?? '') === '1';
if ($early) session_write_close();
if (($_GET['hold'] ?? '') === '1') sleep(5);
if (session_status() === PHP_SESSION_ACTIVE) session_write_close();
echo 'Tamamlandı: ' . $label;
Ayrı ve boş bir yerel deneme dizininde dosyayı lock.php olarak kaydedin. Aşağıdaki komut ve curl akışı POSIX kabuğu bulunan Linux/macOS içindir. Varsayılan php -S tek işçiyle çalışır; istekleri zaten sıraya aldığı için session kilidi deneyini tek başına kanıtlayamaz. PHP’nin desteklenen geliştirme ortamında en az iki işçiyle, yalnız loopback üzerinde başlatın:
Kod:
PHP_CLI_SERVER_WORKERS=2 php -S 127.0.0.1:8000
Bu çok işçili CLI seçeneği Windows’ta desteklenmez. Windows’ta eşzamanlı istek işleyebilen ayrı bir test web sunucusu veya Linux geliştirme ortamı kullanın; tek işçili sunucudaki beklemeyi oturum kilidi sanmayın. Geliştirme sunucusunu genel ağa açmayın. Aynı oturum çerezini kullanarak önce oturumu oluşturun, ardından iki isteği başlatın:
Kod:
curl -s -c cookie.txt "http://127.0.0.1:8000/lock.php?label=initial" > /dev/null
curl -s -b cookie.txt "http://127.0.0.1:8000/lock.php?label=uzun&hold=1" > ilk.txt &
sleep 1
curl -s -b cookie.txt "http://127.0.0.1:8000/lock.php?label=ikinci"
wait
cat ilk.txt
Beklenen davranış, ikinci
curl komutunun hemen tamamlanmaması ve ilk istek yaklaşık beş saniyelik beklemesini bitirip session_write_close() çağırana kadar beklemesidir. Sonra uzun isteğin URL’sine early=1 ekleyip aynı denemeyi tekrarlayın. Oturum bu kez sleep öncesinde kapandığı için ikinci istek ilk isteğin beş saniyelik beklemesini beklememelidir. Aynı denemede farklı oturum çerezi kullanmak da karşılaştırma sağlar. Ağ, işçi sayısı ve zamanlama değişebileceğinden yalnız toplam süreyi kesin kanıt saymayın.| Deneme | Beklenen davranış | Yorum |
|---|---|---|
| Aynı oturum, geç kapatma | İkinci istek session_start içinde bekleyebilir | Oturum kilidi adayı |
| Aynı oturum, early=1 | Uzun işlem sürerken ikinci istek ilerleyebilir | Kilidi erken bırakmanın etkisi |
| Tek sunucu işçisi | İki denemede de istekler sıraya girebilir | Sunucu eşzamanlılığı ayrı darboğaz |
Uygulanabilir kararlar
- İsteğin başında yalnızca gerçekten gereken oturum verilerini okuyun veya güncelleyin.
- Oturum verisiyle işiniz biter bitmez
session_write_close()çağırın. Bundan sonra$_SESSIONüzerinde yaptığınız değişiklikler kaydedilmeyeceği için gerekli tüm yazmaları bu çağrıdan önce tamamlayın. - Ağ isteği, uzun sorgu veya yoğun hesaplama yapacaksanız bunları oturum kilidi kapandıktan sonra yürütün. İşlem sırasında oturum bilgisine yeniden ihtiyaç duyarsanız akışı iki bölüme ayırın.
- İşlemin ilerlemesini oturum değişkenine sürekli yazmak yerine ayrı bir iş durumu veya görev depolama yaklaşımı kullanmayı değerlendirin. Böylece her durum güncellemesinde kullanıcı oturumunu uzun süre kilitlemezsiniz.
- Aynı oturumla gelen AJAX çağrılarını tarayıcı geliştirici araçlarında inceleyin. Bir isteğin süresi uzunken diğerlerinin beklemesi, oturum kilidi çekişmesine işaret edebilir; ancak veritabanı, ağ ve PHP çalışanı sınırlarını da ayrıca kontrol edin.
İstek yalnızca oturum verisini okuyacaksa oturumu okuma sonrasında kapatmaya yönelik PHP seçenekleri kullanılabilir; fakat bu yaklaşımda oturum üzerinde yapılacak değişikliklerin kaydedilmeyeceği varsayılmalıdır. Yazma gereken yerlerde açıkça
session_write_close() kullanmak davranışı daha görünür kılar.Sık yapılan hatalar
- Sadece veri okuduğu için kilit alınmayacağını düşünmek: Varsayılan dosya handler’ında salt okuma amacı da diğer isteğin beklemesine yol açabilir.
- İsteğin sonunda kilidin kendiliğinden bırakılmasını yeterli görmek: Uzun işlemden sonra bırakmak, işlem boyunca tüm paralel istekleri bekletir.
- Oturumu erken kapattıktan sonra yapılan
$_SESSIONdeğişikliğinin kaydedileceğini sanmak: Yazma işlemleri kapatma çağrısından önce yapılmalıdır. - Her yavaş isteği oturum kilidine bağlamak: Aynı veritabanı sorgusu, dış servis, PHP çalışanı havuzu veya istemci bağlantısı da benzer belirti oluşturabilir.
- Özel session handler’ın dosya handler’ı gibi davrandığını varsaymak: Kilitleme, eşzamanlılık ve yazma garantileri kullanılan handler’ın belgelerine ve uygulamasına göre doğrulanmalıdır.
Kaynaklar
- PHP: Dosya oturumlarının kilitlenmesi
- PHP: session_write_close
- PHP: Geliştirme sunucusu ve çoklu işçi