---
title: 'Sunucu Gönderimli Olaylar'
source: 'https://academia.sh/tr/kurslar/eszamansiz-isleme/sunucu-gonderimli-olaylar'
course: 'Önbellekleme, Kuyruklar ve Eşzamansız İşleme'
language: tr
updated: '2026-08-17T18:06:48+00:00'
license: 'CC BY-SA 4.0'
---

# Sunucu Gönderimli Olaylar

Tek bir açık yanıt gövdesine peş peşe yazılan olaylar: olay akışının satır biçimi, akış yanıtının başlık kuralları, olay kimliğiyle yeniden bağlanma ve kalp atışı satırının ara katmanlara karşı işlevi.

Uzun yoklama, bağlantıyı tutup tek bir yanıt yazdıktan sonra kapatıyordu. Rezervasyon
sırasındaki konumun beş kez değişmesi, beş ayrı bağlantı demekti: her olay için istemci
yeniden bağlanıyor, sunucu yeniden yetkilendiriyor, ara katmanlar yeniden yönlendiriyordu.

Oysa ilk bağlantı zaten açıktı. HTTP yanıt gövdesinin bir kerede yazılması zorunlu
değildir; gövde açık tutulup üzerine parça parça yazılabilir. **Sunucu gönderimli
olaylar** (Server-Sent Events, SSE) bunu standartlaştırır: sunucudan istemciye tek yönlü,
metin tabanlı, üzerinde birden çok olay taşıyan bir akış.

## Biçim

Akışın gövdesi satır tabanlıdır ve `text/event-stream` içerik türüyle işaretlenir. Bir
olay, alan satırlarından oluşur ve **boş bir satırla** biter. Beş alan tanımlıdır.

`data:` olayın gövdesidir ve tek zorunlu alandır; birden çok `data:` satırı satır
sonlarıyla birleştirilir. `event:` olayın türünü verir; istemci farklı türler için farklı
işleyiciler bağlayabilir. `id:` olayın kimliğidir ve akışın kaldığı yeri işaretler.
`retry:` istemcinin kopma sonrası kaç milisaniye bekleyeceğini bildirir. İki nokta
üst üste ile başlayan satır ise yorumdur; hiçbir olay üretmez ve **kalp atışı** olarak
kullanılır.

Biçimin tamamı düz metindir, dolayısıyla akış `curl` ile doğrudan okunabilir. Bu, hata
ayıklama açısından SSE'nin en pratik yanıdır.

## Sunucu

Aşağıdaki sunucu, bir üyenin rezervasyon sırasındaki konumunu yayınlıyor: beş adım
ilerleme ve kitabın hazır olduğunu bildiren bir kapanış olayı.

```js
// sse.mjs — rezervasyon sirasinin ilerlemesini sunucu gonderimli olaylarla yayar
import { createServer } from "node:http";

// Uyenin rezervasyon sirasindaki konumu; son olay istegin karsilandigini bildirir.
const OLAYLAR = [
  { id: 1, tur: "sira", veri: { sira: 5, tahmin_dk: 12 } },
  { id: 2, tur: "sira", veri: { sira: 4, tahmin_dk: 9 } },
  { id: 3, tur: "sira", veri: { sira: 3, tahmin_dk: 7 } },
  { id: 4, tur: "sira", veri: { sira: 2, tahmin_dk: 4 } },
  { id: 5, tur: "sira", veri: { sira: 1, tahmin_dk: 2 } },
  { id: 6, tur: "hazir", veri: { kitap: "Tutunamayanlar", sube: 3 } },
];

const sayac = { baglanti: 0, olay: 0 };
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));

createServer(async (istek, yanit) => {
  yanit.sendDate = false;
  const adres = new URL(istek.url, "http://yerel");

  if (adres.pathname === "/olcum") {
    yanit.writeHead(200, { "content-type": "application/json; charset=utf-8" });
    return yanit.end(JSON.stringify(sayac) + "\n");
  }
  if (adres.pathname !== "/akis") return yanit.writeHead(404).end();

  sayac.baglanti++;
  // Akis yaniti: govde bitmez, bu yuzden uzunluk bildirilmez ve ara katmanlarin
  // yanit tamponlamasi kapatilir.
  yanit.writeHead(200, {
    "content-type": "text/event-stream; charset=utf-8",
    "cache-control": "no-store",
    "x-accel-buffering": "no",
  });

  // Istemci kaldigi yeri Last-Event-ID basligiyla bildirir; yoksa bastan baslar.
  const kaldigiYer = Number(istek.headers["last-event-id"] ?? 0);
  const kes = Number(adres.searchParams.get("kes") ?? 0);

  yanit.write("retry: 2000\n\n");                  // yeniden baglanma araligi (ms)
  yanit.write(": nabiz\n\n");                      // yorum satiri: baglantiyi canli tutar

  let gonderilen = 0;
  for (const o of OLAYLAR.filter((o) => o.id > kaldigiYer)) {
    if (kes && gonderilen === kes) break;          // baglanti kopmasinin benzetimi
    await bekle(50);
    if (yanit.writableEnded) return;               // istemci cekildi
    yanit.write(`id: ${o.id}\nevent: ${o.tur}\ndata: ${JSON.stringify(o.veri)}\n\n`);
    sayac.olay++; gonderilen++;
  }
  yanit.end();
}).listen(8371, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8371"));
```

## Akışın Okunması

`curl -N` seçeneği çıktı tamponlamasını kapatır ve akışı geldiği gibi gösterir. Aşağıdaki
betik önce akışı üç olayda kestiriyor, sonra kaldığı yerden sürdürüyor. Bağlantı noktası
8371 keyfîdir ve boş olmalıdır.

```bash
#!/usr/bin/env bash
# sse.mjs'yi baslatir, akisi kesintili okur, Last-Event-ID ile devam eder, durdurur.
node sse.mjs > /dev/null & sunucu=$!
sleep 1
A=http://127.0.0.1:8371

echo "--- ilk baglanti: uc olaydan sonra sunucu kapatiyor ---"
curl -sN "$A/akis?kes=3"
echo "--- yeniden baglanti: Last-Event-ID ile kaldigi yerden ---"
curl -sN -H 'Last-Event-ID: 3' "$A/akis"
echo "--- olcum ---"
curl -sS "$A/olcum"
kill "$sunucu"
```

```
--- ilk baglanti: uc olaydan sonra sunucu kapatiyor ---
retry: 2000

: nabiz

id: 1
event: sira
data: {"sira":5,"tahmin_dk":12}

id: 2
event: sira
data: {"sira":4,"tahmin_dk":9}

id: 3
event: sira
data: {"sira":3,"tahmin_dk":7}

--- yeniden baglanti: Last-Event-ID ile kaldigi yerden ---
retry: 2000

: nabiz

id: 4
event: sira
data: {"sira":2,"tahmin_dk":4}

id: 5
event: sira
data: {"sira":1,"tahmin_dk":2}

id: 6
event: hazir
data: {"kitap":"Tutunamayanlar","sube":3}

--- olcum ---
{"baglanti":2,"olay":6}
```

Ölçüm satırı asıl kazancı gösteriyor: **altı olay, iki bağlantı**. Kesinti olmasaydı tek
bağlantı yeterdi. Aynı altı olay uzun yoklamayla taşınsaydı altı bağlantı, altı
yetkilendirme ve altı yönlendirme kararı gerekirdi.

## Yeniden Bağlanma

Akış er ya da geç kopar: sunucu yeniden dağıtılır, ara katman zaman aşımına uğrar, ağ
kesilir. SSE'yi uzun yoklamadan ayıran şey, kopmanın **protokolün beklediği bir durum**
olmasıdır.

İki alan bu davranışı yönetir. `retry:` ile bildirilen süre, istemcinin kopma sonrası ne
kadar bekleyeceğini belirler; sunucu bu değeri yükün durumuna göre değiştirebilir ve
böylece yeniden bağlanma fırtınasını yönetir. `id:` ile bildirilen kimlik ise istemcide
saklanır ve yeniden bağlanma isteğinde `Last-Event-ID` başlığı olarak geri gönderilir.
Sunucu bu başlığı okuyup akışa oradan devam eder.

Tarayıcı tarafındaki `EventSource` arayüzü ikisini de kendiliğinden yapar: kopmayı
algılar, `retry` süresi kadar bekler, son gördüğü kimliği başlığa koyar. Sunucunun tek
sorumluluğu, o kimliğin **anlamlı bir sürdürme noktası** olmasını sağlamaktır.

Bu, sunucu tarafında bir gereksinim doğurur. Kimliğe göre sürdürebilmek için olayların
bir süre saklanması gerekir; sunucu yalnız "şu anki durumu" biliyorsa, kopma sırasında
üretilmiş olaylar kaybolur. Uygulamada iki çözüm vardır: son N olayı bellekte tutan bir
halka tampon, ya da olay yerine **durumun tamamını** göndermek. İkincisi kimliği
gereksizleştirir çünkü her olay kendi başına yeterlidir; karşılığında her olay daha
büyüktür.

## Akış Yanıtının Kuralları

Akış yanıtı, olağan bir yanıttan dört noktada ayrılır ve bu farkların üçü başlıklarda
görünür.

Gövdenin uzunluğu **bildirilmez**, çünkü bilinmez; `Content-Length` yazılamaz. Yanıt
saklanmamalıdır; `Cache-Control: no-store` bunu söyler, aksi hâlde bir ara önbellek akışı
tamponlayıp tek parça hâlinde teslim etmeye çalışır. Ara katmanların yanıt tamponlaması
kapatılmalıdır; kod bunu `X-Accel-Buffering` başlığıyla yapıyor, bu başlık standart değil
ama yaygın olarak tanınan bir yönergedir. Dördüncü fark koddadır: her yazmadan önce
istemcinin çekilip çekilmediği sınanır, yoksa sunucu kapalı bir yuvaya yazmayı sürdürür.

Kalp atışı satırının işlevi de buradan gelir. Uzun süre veri akmayan bir bağlantıyı ara
katmanlar ölü sayıp kapatabilir. Düzenli aralıklarla gönderilen bir yorum satırı — hiçbir
olay üretmeyen `: nabiz` — bağlantıyı canlı tutar ve istemci tarafında hiçbir yan etkisi
olmaz.

## Sınırlar

SSE tek yönlüdür. İstemci sunucuya veri gönderemez; bunun için ayrı bir HTTP isteği
açması gerekir. Rezervasyon sırasını izlemek için bu yeterlidir, ama iki tarafın da
sürekli konuştuğu bir düzen için değildir.

İkinci sınır aktarım katmanındadır. HTTP/1.1 üzerinde tarayıcıların aynı kaynağa açtığı
eşzamanlı bağlantı sayısı sınırlıdır ve sürekli açık duran bir akış bu paydan bir tanesini
kalıcı olarak tutar; birkaç sekme açan kullanıcı sınırı doldurabilir. HTTP/2 üzerinde
akışlar tek bağlantıda çoğullandığı için bu sınır ortadan kalkar.

Üçüncü sınır veri biçimidir: akış metindir, ikili veri taşınamaz. İkili veri gerekiyorsa
kodlanması gerekir ve bu, boyutu büyütür.

## Özet

- Sunucu gönderimli olaylar, açık tutulan tek bir HTTP yanıt gövdesine peş peşe yazılan, sunucudan istemciye tek yönlü, metin tabanlı bir akıştır.
- Olay `data`, `event`, `id` ve `retry` alanlarından oluşur ve boş satırla biter; iki nokta üst üste ile başlayan yorum satırı kalp atışı olarak kullanılır.
- Ölçümde altı olay iki bağlantıyla taşındı; aynı olaylar uzun yoklamayla altı bağlantı gerektirirdi.
- Kopma protokolün beklediği bir durumdur: istemci `retry` süresi kadar bekler ve son gördüğü kimliği `Last-Event-ID` başlığıyla gönderir, sunucu akışa oradan devam eder.
- Akış yanıtı uzunluk bildirmez, saklanmamalıdır, ara katman tamponlaması kapatılmalıdır ve her yazmadan önce istemcinin bağlı olduğu sınanmalıdır.

## Sonraki Adım

Akış tek yönlü olduğu sürece rezervasyon sırası için yeterli. Ama kütüphane servisinde
iki tarafın da konuştuğu durumlar var: görevlinin şube stok sayacını düzeltmesi, bir
üyenin sırasından çekilmesi, birden çok görevlinin aynı ödünç kaydını aynı anda düzenlemesi.
Bunların her biri için ayrı bir HTTP isteği açmak, akışın kazandırdığı gecikmeyi geri
verir. Sonraki ders çift yönlü bir bağlantının nasıl kurulduğunu ele alır: HTTP üzerinden
başlayan, el sıkışmadan sonra kendi çerçeve biçimine geçen ve iki yönde de mesaj taşıyan
bir bağlantı.
