İçeriğe geç
academia.sh

Ders 21 / 25

Yoklama Yaklaşımları

İstemcinin sunucudaki değişiklikten haberdar olmasının en yalın yolu: kısa yoklamanın istek ve boş yanıt maliyeti, uzun yoklamanın bağlantı tutma karşılığı, ikisinin aynı olay üzerinde ölçülmesi ve sürüm numarasıyla kapatılan olay boşluğu.

İçindekiler

Bir önceki konu işin sunucu tarafındaki dayanıklılığını tamamladı: iş bir işçiye düşer, zamanında tetiklenir, ilerlemesi kaydedilir, kuyruk taşmaz, aşırı yükte denetimli olarak reddedilir. Zincirin bir ucu hâlâ açık kaldı. İşi başlatan istemci, işin kabul edildiğini biliyor ama sonucun ne zaman hazır olduğunu bilmiyor.

Uzun süren raporun ilerleme yüzdesi, rezervasyon sırasındaki konumun değişmesi, şube stok sayacının güncellenmesi — bunların hepsi sunucu tarafında hazır ama istemci tarafında görünmez. HTTP’nin varsayılan yönü bunu zorlaştırır: bağlantıyı istemci açar, isteği istemci gönderir. Sunucunun kendiliğinden konuşma imkânı yoktur.

Bu konu, bilginin istemciye ulaşmasının dört yolunu kurar. En yalınından başlıyoruz: istemcinin tekrar tekrar sorması.

Yoklama

Yoklama (polling), istemcinin durumu öğrenmek için sunucuya düzenli aralıklarla istek göndermesidir. Hiçbir ek protokol gerektirmez; sunucu tarafında ek durum tutulmaz; her ara katman, güvenlik duvarı ve önbellek onu olağan bir HTTP isteği olarak görür. Bu yüzden yoklama, gerçek zamanlı veri sorununun varsayılan yanıtıdır ve pek çok durumda doğru yanıttır.

İki biçimi vardır ve aralarındaki fark tek bir karardır: sunucu, verecek bir şeyi yoksa ne yapar.

Kısa yoklama (short polling) biçiminde sunucu hemen cevap verir. Değişiklik yoksa “değişiklik yok” der ve bağlantı kapanır; istemci bir süre bekleyip yeniden sorar.

Uzun yoklama (long polling) biçiminde sunucu cevabı geciktirir. Bir değişiklik olana kadar bağlantıyı açık tutar; değişiklik gelirse cevabı o anda yazar. Belirli bir süre boyunca hiçbir şey olmazsa boş cevap verir ve istemci yeniden bağlanır.

İki Yaklaşımı Tek Sunucuda Kurmak

Aşağıdaki sunucu, önceki konuda is_durumu tablosuna yazılan ilerlemeyi sunuyor. Durumun bir sürüm numarası var; istemci en son gördüğü sürümü isteğe iliştiriyor.

// yoklama.mjs — rapor isinin durumunu iki bicimde sunar: kisa yoklama ve uzun yoklama
import { createServer } from "node:http";

const TUTMA_SINIRI = 1000;         // uzun yoklamanin baglantiyi tutma suresi (ms)
let surum = 0, ilerleme = 0;       // rapor-2026-01 isinin durumu
const sayac = { durum_istegi: 0, bekle_istegi: 0, bos_yanit: 0 };
const bekleyen = new Set();        // acik tutulan uzun yoklama yanitlari

const govde = () => JSON.stringify({ surum, ilerleme }) + "\n";
const yaz = (yanit, metin, kod = 200) => {
  yanit.writeHead(kod, { "content-type": "application/json; charset=utf-8" });
  yanit.end(metin);
};
const bos = (yanit) => { sayac.bos_yanit++; yanit.writeHead(204).end(); };

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const adres = new URL(istek.url, "http://yerel");
  const gorulen = Number(adres.searchParams.get("surum") ?? 0);

  if (adres.pathname === "/durum") {              // kisa yoklama: hemen cevap ver
    sayac.durum_istegi++;
    return surum > gorulen ? yaz(yanit, govde()) : bos(yanit);
  }

  if (adres.pathname === "/bekle") {              // uzun yoklama: degisene kadar tut
    sayac.bekle_istegi++;
    if (surum > gorulen) return yaz(yanit, govde());
    const kayit = { yanit };
    kayit.zamanlayici = setTimeout(() => { bekleyen.delete(kayit); bos(yanit); },
                                   TUTMA_SINIRI);
    return void bekleyen.add(kayit);
  }

  if (adres.pathname === "/ilerlet") {            // isci ilerlemeyi bildirdi
    surum++; ilerleme += 10;
    for (const b of bekleyen) { clearTimeout(b.zamanlayici); yaz(b.yanit, govde()); }
    bekleyen.clear();
    return yaz(yanit, govde());
  }

  if (adres.pathname === "/olcum")
    return yaz(yanit, JSON.stringify(sayac) + "\n");

  yaz(yanit, JSON.stringify({ hata: "yol yok" }) + "\n", 404);
}).listen(8361, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8361"));

İki uç arasındaki tek yapısal fark bekleyen kümesidir. Kısa yoklamada sunucu isteği işleyip unutur; uzun yoklamada yanıt nesnesini saklar ve /ilerlet geldiğinde o saklanan nesnelere yazar. Yani uzun yoklama, sunucuya durum ekler.

Ölçüm

Aşağıdaki betik aynı tek olayı — raporun bir adım ilerlemesini — iki yaklaşımla karşılar ve sayar. Bağlantı noktası 8361 keyfîdir ve boş olmalıdır.

#!/usr/bin/env bash
# yoklama.mjs'yi baslatir, ayni tek olayi iki yaklasimla olcer, durdurur.
A=http://127.0.0.1:8361
B='%{http_code} %{size_download} B\n'

node yoklama.mjs > /dev/null & sunucu=$!
sleep 1
echo "--- kisa yoklama: alti istek ---"
for i in 1 2 3 4 5; do curl -sS -w "$B" "$A/durum?surum=0"; done
curl -sS -o /dev/null -X POST "$A/ilerlet"          # is bu anda ilerledi
curl -sS -w "$B" "$A/durum?surum=0"
curl -sS "$A/olcum"
kill "$sunucu"; sleep 0.3

node yoklama.mjs > /dev/null & sunucu=$!
sleep 1
echo "--- uzun yoklama: iki istek ---"
curl -sS -w "$B" "$A/bekle?surum=0" & bekleyen=$!
sleep 0.3
curl -sS -o /dev/null -X POST "$A/ilerlet"          # is bu anda ilerledi
wait $bekleyen
curl -sS -w "$B" "$A/bekle?surum=1"                 # degisim yok: sure dolar
curl -sS "$A/olcum"
kill "$sunucu"
--- kisa yoklama: alti istek ---
204 0 B
204 0 B
204 0 B
204 0 B
204 0 B
{"surum":1,"ilerleme":10}
200 26 B
{"durum_istegi":6,"bekle_istegi":0,"bos_yanit":5}
--- uzun yoklama: iki istek ---
{"surum":1,"ilerleme":10}
200 26 B
204 0 B
{"durum_istegi":0,"bekle_istegi":2,"bos_yanit":1}

Aynı olay, kısa yoklamada altı istekle, uzun yoklamada iki istekle karşılandı. Kısa yoklamada beş yanıt boştu; boş yanıtların gövdesi sıfır bayt olduğu için taşınan veri küçük, ama istek sayısı aynı kaldı. Her istek bir bağlantı kurulumu, bir yönlendirme kararı, bir kimlik doğrulama ve bir kütük satırı demektir; gövdenin boş olması bunların hiçbirini ucuzlatmaz.

Uzun yoklamada ikinci istek bir saniye açık kaldı ve değişiklik olmadığı için 204 ile kapandı. Bu, uzun yoklamanın kaçınılmaz maliyetidir: bağlantı sonsuza kadar tutulamaz, çünkü ara katmanların ve yük dağıtıcıların kendi zaman aşımları vardır.

Maliyetin Hesabı

İki yaklaşımın maliyeti, gözlem penceresi ve olay sıklığıyla hesaplanabilir. Aşağıdaki tablo altmış saniyelik bir pencerede tek bir olay olduğu varsayımıyla çıkarılmıştır.

Yaklaşım İstek Boş yanıt En kötü gecikme
Kısa yoklama, 1 s arayla 60 59 1 s
Kısa yoklama, 5 s arayla 12 11 5 s
Uzun yoklama, 30 s tutma 3 1 ağ gecikmesi

Kısa yoklamada istek sayısı pencere / aralık ile, en kötü gecikme ise doğrudan aralıkla belirlenir. İkisi ters orantılıdır ve bu, kısa yoklamanın tek gerçek ödünleşimidir: gecikmeyi yarıya indirmek istek sayısını iki katına çıkarır.

Uzun yoklamada istek sayısı pencere / tutma süresi + olay sayısı ile belirlenir ve gecikme aralığa bağlı değildir. Bu yüzden uzun yoklama, seyrek ama gecikmeye duyarlı olaylar için kısa yoklamadan belirgin ölçüde ucuzdur.

Hesabın yönü olay sıklığı arttıkça tersine döner. Saniyede birden çok olay üreten bir kaynakta uzun yoklama her olayda bir bağlantı kurar; kısa yoklama ise bir istekte birden çok değişikliği toplu olarak taşır. Sık değişen ve her ara değeri önemsenmeyen veriler — şube stok sayacı gibi — kısa yoklamayla daha ucuza sunulur.

Sunucu Tarafındaki Bedel

Uzun yoklama sunucuya durum ekler ve bu durumun bir sınırı vardır. Açık tutulan her istemci bir yuva (socket), bir dosya tanıtıcısı ve bir yanıt nesnesi tutar. Bin istemci bin açık bağlantı demektir; bunlar iş yapmıyor olsa da kaynak tüketirler.

Üç sonuç doğar. Sunucunun eşzamanlı bağlantı sınırı, artık eşzamanlı istek sınırından farklı bir büyüklüktür ve ona göre ayarlanmalıdır. Önceki dersteki kabul denetimi bu bağlantıları saymamalıdır; bekleyen bir uzun yoklama işlemci kullanmaz, dolayısıyla onu “çalışan iş” olarak saymak sunucuyu gereksiz yere doygun gösterir. Ve tutma süresi, ara katmanların zaman aşımından kısa seçilmelidir; aksi hâlde bağlantıyı sunucu değil aradaki bir katman keser ve istemci nedenini anlayamaz.

Olay Boşluğu

Uzun yoklamanın gözden kaçan kusuru şurasıdır: yanıt yazıldıktan sonra istemci yeniden bağlanana kadar geçen sürede sunucu dinlenmez. O aralıkta olan bir olay, hiçbir açık bağlantıya yazılamaz.

Kodda bu boşluğu kapatan şey sürüm numarasıdır. İstemci en son gördüğü sürümü isteğe ekler; sunucu surum > gorulen koşulunu bağlantıyı tutmadan önce sınar ve geride kalmış bir istemciye hemen cevap verir. Ölçümdeki ikinci uzun yoklamanın surum=1 ile gönderilmesinin nedeni budur.

Bu, gerçek zamanlı veri sunan her yaklaşımın ortak gereksinimidir: istemcinin nerede kaldığını söyleyebilmesi. Sürüm numarası, olay kimliği ya da damga — adı değişir, işlevi değişmez. Sonraki derste aynı işi Last-Event-ID başlığının yaptığını göreceğiz.

Özet

  • Yoklama, istemcinin durumu öğrenmek için düzenli aralıklarla istek göndermesidir; ek protokol gerektirmediği için varsayılan yaklaşımdır.
  • Kısa yoklamada sunucu hemen cevap verir ve boş yanıtlar üretir; istek sayısı pencere / aralık ile artar, en kötü gecikme aralığın kendisidir.
  • Uzun yoklamada sunucu değişiklik olana kadar bağlantıyı tutar; ölçümde aynı olay altı istek yerine iki istekle karşılandı ve gecikme aralığa bağlı olmaktan çıktı.
  • Uzun yoklamanın bedeli sunucuya eklenen durumdur: açık tutulan her bağlantı kaynak tüketir, tutma süresi ara katman zaman aşımlarından kısa seçilmelidir.
  • İki yoklama arasındaki olay boşluğu, istemcinin en son gördüğü sürümü isteğe iliştirmesiyle kapatılır.

Sonraki Adım

Uzun yoklama, bağlantıyı tutup tek bir yanıt yazdıktan sonra kapatıyor. Her olay için yeni bir bağlantı kurulması, olaylar sıklaştığında saçma bir düzene dönüşür: aynı istemciye arka arkaya yazılacak on olay için on ayrı bağlantı. Oysa bağlantı zaten açıkken üzerine birden çok olay yazılabilir. Sonraki ders bunu standartlaştıran biçimi kurar: sunucunun tek bir açık yanıt gövdesine olayları peş peşe yazdığı, tarayıcı tarafında yeniden bağlanmayı kendi üstlenen tek yönlü akış.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat