---
title: "Eşzamanlı ve Eşzamansız API'ler"
source: 'https://academia.sh/tr/kurslar/api-tasarimi/eszamanli-ve-eszamansiz-apiler'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:45+00:00'
license: 'CC BY-SA 4.0'
---

# Eşzamanlı ve Eşzamansız API'ler

İstek–yanıt kalıbının dışına çıkan iletişim biçimleri: olay akışı, uzun yoklama ve kısa yoklama aynı olay dizisinde bağlantı sayısı, taşınan bayt ve teslim gecikmesiyle karşılaştırılır; uzun süren iş kabul edilen istek ve ayrı bir durum kaynağıyla modellenir.

Buraya kadarki beş biçemin ortak bir varsayımı vardı: iletişimi istemci başlatır, sunucu
yanıt verir, alışveriş biter. Kütüphane servisinin bazı işleri bu kalıba girmez.

Üç durum bu kalıbı kırar. Birincisi, **olay sunucuda oluşur**: bir kitabın rafa dönmesini
bekleyen istemciye kimse haber vermez, istemci de ne zaman soracağını bilmez. İkincisi,
**iş uzun sürer**: şube sayımı dakikalarca çalışır ve isteği o süre boyunca açık tutmak
bağlantıyı da istemciyi de bekletir. Üçüncüsü, **aynı bildirim çok sayıda tüketiciye gider**:
her tüketicinin ayrı ayrı sorması aynı işi tekrarlar.

Bu ders ilk durumu üç biçimde yazıp ölçer, sonra ikinci durumun kalıbını kurar.

## Aynı Olayı Üç Biçimde Yayımlamak

Sunucu altı olay yayımlar — kitapların rafa dönüşü — ve aynı olay dizisini üç ayrı uçtan
sunar. **Olay akışı** bağlantıyı açık tutar ve olay oluştukça yazar. **Uzun yoklama** isteği
yeni olay çıkana kadar yanıtsız bekletir. **Kısa yoklama** her isteği hemen yanıtlar; boş
dönebilir, istemci belirli aralıklarla yeniden sorar.

Üçünde de istemci en son gördüğü olayın kimliğini gönderir. Bu, yoklama biçimlerinde bir
zorunluluktur: iki istek arasındaki boşlukta oluşan olaylar aksi hâlde kaybolur.

```js
// sunucu.mjs — ayni olay akisini uc bicimde yayimlar: olay akisi, uzun yoklama, kisa yoklama
import { createServer } from "node:http";

const OLAYLAR = [];              // yayimlanmis olaylar; kimlik dizideki sira numarasidir
const bekleyenler = [];          // uzun yoklamada yanit bekleyen istekler
const akislar = new Set();       // acik olay akisi baglantilari
const isler = new Map();         // uzun suren islerin durumu
let isSayaci = 0;

const yayimla = (olay) => {
  OLAYLAR.push(olay);
  for (const y of akislar) y.write(`id: ${OLAYLAR.length}\ndata: ${JSON.stringify(olay)}\n\n`);
  while (bekleyenler.length) bekleyenler.pop()();
};

const json = (yanit, govde) => {
  const metin = JSON.stringify(govde);
  yanit.setHeader("Content-Type", "application/json; charset=utf-8");
  yanit.setHeader("Content-Length", Buffer.byteLength(metin));
  yanit.writeHead(200).end(metin);
};
const sonrasi = (n) => OLAYLAR.slice(n).map((o, i) => ({ id: n + i + 1, ...o }));

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

  // 1) Olay akisi: baglanti acik kalir, olay olustukca sunucu yazar.
  if (adres.pathname === "/akis") {
    yanit.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-store",
                           Connection: "keep-alive" });
    for (const o of sonrasi(son)) yanit.write(`id: ${o.id}\ndata: ${JSON.stringify(o)}\n\n`);
    akislar.add(yanit);
    istek.on("close", () => akislar.delete(yanit));
    return;
  }
  // 2) Uzun yoklama: yeni olay yoksa yanit bekletilir, olay gelince yazilir.
  if (adres.pathname === "/uzun") {
    if (OLAYLAR.length > son) return json(yanit, { olaylar: sonrasi(son) });
    const zamanAsimi = setTimeout(() => {
      const k = bekleyenler.indexOf(surdur); if (k >= 0) bekleyenler.splice(k, 1);
      json(yanit, { olaylar: [] });
    }, 2000);
    const surdur = () => { clearTimeout(zamanAsimi); json(yanit, { olaylar: sonrasi(son) }); };
    bekleyenler.push(surdur);
    return;
  }
  // 3) Kisa yoklama: her istek hemen yanitlanir, istemci belli araliklarla sorar.
  if (adres.pathname === "/kisa") return json(yanit, { olaylar: sonrasi(son) });
  // 4) Uzun suren is: istek kabul edilir, sonuc ayri bir kaynaktan izlenir.
  if (adres.pathname === "/sayimlar" && istek.method === "POST") {
    const kimlik = `is-${++isSayaci}`;
    isler.set(kimlik, { durum: "calisiyor", sonuc: null });
    setTimeout(() => isler.set(kimlik, { durum: "bitti", sonuc: { eksik: 3 } }), 600);
    yanit.setHeader("Location", `/isler/${kimlik}`);
    yanit.setHeader("Content-Type", "application/json; charset=utf-8");
    return yanit.writeHead(202).end(JSON.stringify({ is: kimlik }));
  }
  if (adres.pathname.startsWith("/isler/")) return json(yanit, isler.get(adres.pathname.slice(7)));
  // Olculerin ayni kosulda calismasi icin yayin disaridan baslatilir.
  if (adres.pathname === "/basla") { basla(); return json(yanit, { baslatildi: true }); }
  return json(yanit, { hata: "bulunamadi" });
}).listen(8491, "127.0.0.1", () => console.log("olay ucu 127.0.0.1:8491"));

// Alti olay, 250 ms araliklarla: bir kitabin rafa donmesi sunucuda olusan bir olaydir.
const basla = () => {
  let n = 0;
  const t = setInterval(() => {
    yayimla({ tur: "kitap_rafa_dondu", isbn: `978-000000000${n}`, an: Date.now() });
    if (++n === 6) clearInterval(t);
  }, 250);
};
```

Ölçümün geçerli olması için üç istemcinin de aynı olay dizisini, aynı anda izlemesi gerekir.
Bu yüzden yayın kendiliğinden başlamaz: üç istemci bağlandıktan sonra dışarıdan tetiklenir.

```js
// olc.mjs — uc bicimi ayni olay akisinda, ayni anda olcer: baglanti sayisi, bayt, teslim gecikmesi
import { Agent, request } from "node:http";
import { connect } from "node:net";

const olustur = () => {
  const durum = { baglanti: 0, istek: 0, bayt: 0, gecikmeler: [] };
  const ajan = new Agent({ keepAlive: false });     // her istek yeni baglanti acar
  ajan.createConnection = (s) => {
    durum.baglanti++;
    const b = connect(s);
    b.on("data", (p) => (durum.bayt += p.length));
    return b;
  };
  return { durum, ajan };
};
const kaydet = (durum, olaylar) => {
  for (const o of olaylar) durum.gecikmeler.push(Date.now() - o.an);
  return olaylar.length ? olaylar[olaylar.length - 1].id : null;
};
const al = (birim, yol) => new Promise((coz) => {
  birim.durum.istek++;
  request({ agent: birim.ajan, host: "127.0.0.1", port: 8491, path: yol }, (y) => {
    let m = ""; y.on("data", (p) => (m += p)); y.on("end", () => coz(JSON.parse(m)));
  }).end();
});

// 1) OLAY AKISI — tek baglanti acik kalir, sunucu yazdikca istemci okur
const akis = olustur();
const akisIsi = new Promise((bitti) => {
  akis.durum.istek++;
  request({ agent: akis.ajan, host: "127.0.0.1", port: 8491, path: "/akis?son=0" }, (y) => {
    y.on("data", (p) => {
      for (const blok of String(p).split("\n\n")) {
        const e = /data: (.+)/.exec(blok);
        if (e) kaydet(akis.durum, [JSON.parse(e[1])]);
      }
      if (akis.durum.gecikmeler.length === 6) { y.destroy(); bitti(); }
    });
  }).end();
});

// 2) UZUN YOKLAMA — sunucu yanit vermeden bekletir, olay gelince yazar; istemci yeniden baglanir
const uzun = olustur();
const uzunIs = (async () => {
  let son = 0;
  while (uzun.durum.gecikmeler.length < 6)
    son = kaydet(uzun.durum, (await al(uzun, `/uzun?son=${son}`)).olaylar) ?? son;
})();

// 3) KISA YOKLAMA — sabit aralikla sorma
const kisa = olustur();
const kisaIs = (async () => {
  let son = 0;
  while (kisa.durum.gecikmeler.length < 6) {
    son = kaydet(kisa.durum, (await al(kisa, `/kisa?son=${son}`)).olaylar) ?? son;
    if (kisa.durum.gecikmeler.length < 6) await new Promise((c) => setTimeout(c, 500));
  }
})();

await new Promise((c) => setTimeout(c, 200));       // uc istemci de baglandiktan sonra yayin baslar
await fetch("http://127.0.0.1:8491/basla");
await Promise.all([akisIsi, uzunIs, kisaIs]);

const ozet = (ad, durum) => {
  const g = durum.gecikmeler;
  console.log(`  ${ad.padEnd(13)} baglanti=${String(durum.baglanti).padStart(2)}  ` +
    `bayt=${String(durum.bayt).padStart(4)}  olay=${g.length}  ` +
    `ortalama gecikme=${String(Math.round(g.reduce((t, v) => t + v, 0) / g.length)).padStart(3)} ms  ` +
    `en buyuk=${String(Math.max(...g)).padStart(3)} ms`);
};
console.log("-- alti olay, 250 ms araliklarla yayimlandi --");
ozet("olay akisi", akis.durum);
ozet("uzun yoklama", uzun.durum);
ozet("kisa yoklama", kisa.durum);

// 4) UZUN SUREN IS — istek kabul edilir, sonuc ayri bir kaynaktan izlenir
console.log("\n-- uzun suren is: kabul edilen istek --");
const t0 = Date.now();
const kabul = await fetch("http://127.0.0.1:8491/sayimlar", { method: "POST" });
const yer = kabul.headers.get("location");
console.log(`  POST /sayimlar -> HTTP ${kabul.status}  Location: ${yer}`);
for (let i = 0; i < 5; i++) {
  const d = await (await fetch(`http://127.0.0.1:8491${yer}`)).json();
  console.log(`  GET ${yer} (+${Date.now() - t0} ms) -> ${JSON.stringify(d)}`);
  if (d.durum === "bitti") break;
  await new Promise((c) => setTimeout(c, 200));
}
```

```bash
node sunucu.mjs > /dev/null & p=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null "http://127.0.0.1:8491/kisa?son=0"
node olc.mjs
kill $p
```

```
-- alti olay, 250 ms araliklarla yayimlandi --
  olay akisi    baglanti= 1  bayt= 663  olay=6  ortalama gecikme=  3 ms  en buyuk=  5 ms
  uzun yoklama  baglanti= 6  bayt=1170  olay=6  ortalama gecikme=  2 ms  en buyuk=  4 ms
  kisa yoklama  baglanti= 5  bayt=1055  olay=6  ortalama gecikme=165 ms  en buyuk=292 ms

-- uzun suren is: kabul edilen istek --
  POST /sayimlar -> HTTP 202  Location: /isler/is-1
  GET /isler/is-1 (+14 ms) -> {"durum":"calisiyor","sonuc":null}
  GET /isler/is-1 (+219 ms) -> {"durum":"calisiyor","sonuc":null}
  GET /isler/is-1 (+425 ms) -> {"durum":"calisiyor","sonuc":null}
  GET /isler/is-1 (+629 ms) -> {"durum":"bitti","sonuc":{"eksik":3}}
```

Gecikme değerleri ve bayt sayıları makineye ve çalışma zamanının yazdığı başlıklara bağlıdır;
altı olayın sayısı, bağlantı sayıları ve büyüklük sıralaması bağlı değildir.

## Ölçümün Söyledikleri

**Olay akışı tek bağlantıyla altı olayı taşır.** İstemci bir kez bağlanır, sunucu yazdıkça
okur; gecikme milisaniye düzeyindedir. Taşınan bayt da en düşüktür, çünkü altı olay için tek
bir başlık kümesi gönderilmiştir.

**Uzun yoklama aynı gecikmeyi altı bağlantıyla elde eder.** Her olaydan sonra yanıt kapanır
ve istemci yeniden bağlanır; her yeniden bağlanma yeni bir istek satırı ve yeni bir başlık
kümesi demektir. Bayt sayısı olay akışının yaklaşık iki katıdır. Gecikme düşüktür, çünkü
sunucu isteği olay gelene kadar bekletmiştir — beklemenin bedeli baytta değil, açık tutulan
istek sayısındadır: her bekleyen istemci sunucuda bir yanıt nesnesi ve bir bağlantı tutar.

**Kısa yoklama gecikmeyi yoklama aralığına bağlar.** Ortalama gecikme 165 milisaniye, en
büyüğü 292 milisaniyedir; yoklama aralığı yarım saniyedir ve beklenen ortalama gecikme
aralığın yarısı kadardır. Bağlantı sayısı burada olay sayısına değil, geçen süreye bağlıdır:
hiç olay olmasa da istemci sormaya devam eder ve her boş yanıt taşınan bayta eklenir.

Üçü arasındaki seçim tek bir ölçüyle yapılmaz. Kısa yoklama en basit olanıdır ve gecikmenin
önemsiz olduğu yerlerde yeterlidir; ara katmanlar için sıradan bir istektir. Uzun yoklama
gecikmeyi düşürür ama sunucuda açık tutulan istek sayısını yükseltir. Olay akışı her ikisinde
de kazanır, karşılığında uzun ömürlü bağlantı ister: araya giren katmanların bağlantıyı
kapatmaması, sunucunun her istemci için bir bağlantı ayırabilmesi ve akışın kopması hâlinde
yeniden kurulabilmesi gerekir.

Son nokta ölçümdeki `son` parametresinin karşılığıdır. **Server-Sent Events**, kopan bir
bağlantıyı yeniden kurarken en son alınan olay kimliğini `Last-Event-ID` başlığıyla geri
gönderir; sunucu da kaldığı yerden devam eder. Bu düzenek olmadan hiçbir eşzamansız yayım
güvenilir değildir: kopma anında oluşan olaylar sessizce kaybolur.

## Tek Yönlü ve Çift Yönlü

Olay akışı tek yönlüdür: sunucudan istemciye. İstemcinin de sürekli veri göndermesi
gerekiyorsa — raf sayımında el uçbiriminin okuduğu her etiketi anında bildirmesi gibi — iki
yönlü bir bağlantı gerekir ve bunun karşılığı **WebSocket** protokolüdür. Bedeli, HTTP'nin
anlamlarından çıkmaktır: yöntem, durum kodu, önbellek yönergesi ve koşullu istek kavramları
bağlantı kurulduktan sonra geçerli değildir. Üçüncü derste ölçülen kazançların hiçbiri açık
soket üzerinde geçerli olmaz.

Seçim ölçütü basittir: veri yalnız sunucudan geliyorsa olay akışı yeterlidir; her iki taraf
da sürekli veri gönderiyorsa çift yönlü bağlantı gerekir.

## Kabul Edilen İstek

Ölçümün son bölümü ikinci durumu kurar. Şube sayımı istendiğinde sunucu işi bitirmeye
çalışmaz; isteği **kabul eder**, `202` döndürür ve `Location` başlığıyla işin izleneceği
kaynağı bildirir. İstemci o kaynağı sorarak durumu öğrenir; iş bitince aynı kaynak sonucu
taşır.

Bu kalıbın üç yararı vardır. İstek süresi işin süresinden ayrılır, dolayısıyla zaman aşımı
sorunu ortadan kalkar. İşin durumu bir kaynak olduğu için tek biçim arayüzün içinde kalır;
listelenebilir, iptal edilebilir, saklanabilir. Ve aynı iş iki kez istendiğinde ne olacağı
ayrı bir karara dönüşür — bu kursun Etkisizlik Anahtarları dersinin konusudur.

İşin bittiğini öğrenmek için istemcinin sorması da zorunlu değildir: durum kaynağı bir olay
akışıyla birleştirildiğinde, iş bittiğinde bildirim istemciye kendiliğinden ulaşır. İki
kalıp birbirinin yerine değil, birlikte kullanılır.

## Özet

- İstek–yanıt kalıbı üç durumda yetmez: olayın sunucuda oluşması, işin uzun sürmesi ve aynı
  bildirimin çok sayıda tüketiciye gitmesi.
- Ölçümde olay akışı altı olayı tek bağlantı ve 663 baytla, uzun yoklama altı bağlantı ve
  1170 baytla taşıdı; ikisinin de gecikmesi milisaniye düzeyinde kaldı.
- Kısa yoklamada gecikme yoklama aralığına bağlıdır: yarım saniyelik aralıkta ortalama 165,
  en büyük 292 milisaniye ölçüldü ve bağlantı sayısı olay sayısına değil geçen süreye bağlandı.
- Her yoklama biçimi son görülen olay kimliğini taşımak zorundadır; kopma ve istekler arası
  boşlukta oluşan olaylar aksi hâlde kaybolur.
- Uzun süren iş `202` ile kabul edilir ve ayrı bir durum kaynağıyla izlenir; böylece istek
  süresi işin süresinden ayrılır ve iş, tek biçim arayüzün içinde bir kaynağa dönüşür.

## Sonraki Adım

Bu konu biçemleri karşılaştırdı ve her birinin neyi kolaylaştırıp neyi zorlaştırdığını ölçtü.
Bundan sonrası bir seçimin ayrıntısıdır: kaynak temelli biçemin gerçekten tasarlanması.
Ölçümlerde kaynaklar hazır varsayıldı — kitap, üye, ödünç, iş — oysa bunların kaynak olması
bir karardı. Alan kavramlarının hangisi bir kaynağa dönüşür, hangisi bir başkasının alanı
olarak kalır? "Kitabı uzat" gibi bir eylem hangi kaynağa oturur? Sonraki konunun ilk dersi
bu soruyla başlar ve kaynak modelini kütüphane alanının kavramlarından çıkarır.
