---
title: 'Gerçek Zamanlı Veri'
source: 'https://academia.sh/tr/kurslar/frontend-mimarisi/gercek-zamanli-veri'
course: 'Uygulama Mimarisi: Yönlendirme, Durum ve Veri'
language: tr
updated: '2026-08-17T18:09:20+00:00'
license: 'CC BY-SA 4.0'
---

# Gerçek Zamanlı Veri

Sunucunun kendiliğinden ürettiği verinin istemciye taşınması; yoklama, olay akışı ve soket arasındaki seçim, akış biçiminin çözümlenmesi, kopan bağlantının kimlikle devam ettirilmesi ve olayların duruma işlenmesi.

Buraya kadar veri hep istemcinin sorduğu anda geldi. Kuzey Yamaç istasyonu ise ölçümü
kimse sormadan üretir: sensör on dakikada bir okuma yapar, sunucu kaydı işler. Ölçüm
geçmişi ekranı açıkken üretilen bir kaydın görünmesi için kullanıcının sayfayı yenilemesi
gerekiyorsa, ekran gerçeği geriden izler.

Bu ders akışın yönünü tersine çeviren taşımaları ele alır. Ortak nokta şudur: bağlantıyı
her zaman istemci kurar — tarayıcıya dışarıdan bağlantı açılamaz — ama veri gönderme
zamanını sunucu belirler. Farkları, bağlantının ne kadar sürdüğünde ve kaç yöne
işlediğindedir.

## Üç Taşıma Yolu

| Yol | Yön | Bağlantı | Yeniden bağlanma | Uygun olduğu iş |
|---|---|---|---|---|
| Yoklama | istemci sorar | her seferinde yeni | gerekmez | seyrek ve gecikmeye toleranslı veri |
| Uzun yoklama | istemci sorar | yanıt gelene dek açık | her yanıttan sonra | olay seyrek, gecikme önemli |
| Olay akışı | sunucu iter | tek uzun HTTP yanıtı | yerleşik | tek yönlü bildirim akışı |
| Soket | iki yönlü | tek uzun bağlantı | elle kurulur | karşılıklı ve sık iletişim |

**Yoklama (polling)**, belirli aralıklarla istek atmaktır. Kurulumu en ucuz yoldur ve
önceki iki dersin istek katmanı olduğu gibi kullanılır. Bedeli iki yerdedir: veri
değişmemişken de istek atılır, ve en kötü durumda gecikme aralık kadardır. Otuz saniyede
bir yoklanan bir ekran, olayı en geç otuz saniye sonra görür. Koşullu istek başlıklarıyla
— Tarayıcı ve Web Platformu kursunda tanıtılan doğrulayıcılarla — gövde aktarımı
azaltılabilir, ama istek sayısı azalmaz.

**Uzun yoklama (long polling)**, sunucunun yanıtı veri oluşana kadar bekletmesidir. İstek
sayısı olay sayısına iner, gecikme kaybolur. Karşılığında her olaydan sonra yeni bir istek
kurulur ve sunucu tarafında açık bekleyen isteklerin tutulması gerekir.

**Olay akışı (Server-Sent Events)**, tek bir HTTP yanıtının kapanmadan sürmesidir. Sunucu
aynı yanıt gövdesine metin parçaları yazmaya devam eder, istemci geldikçe okur. Sıradan
bir HTTP yanıtı olduğu için ara katmanlarla — vekil sunucular, sıkıştırma, kimlik
doğrulama başlıkları — uyumludur. Yalnız tek yönlüdür: istemci bu kanaldan bir şey
gönderemez, göndermek istediğinde ayrı bir istek atar.

**Soket (WebSocket)**, HTTP üzerinden başlayıp protokol değiştirerek kurulan çift yönlü
bir kanaldır. İki taraf da istediği an ileti gönderir. Karşılıklı etkileşim gerektiren
işlerde — birlikte düzenleme, canlı imleç, oyun — tek seçenektir.

Seçim kuralı sadedir: veri tek yönde akıyorsa olay akışı yeterlidir ve daha az parça
gerektirir. İstemcinin de aynı kanaldan sık ileti göndermesi gerekiyorsa soket kurulur.

## Olay Akışının Biçimi

Akış, satır tabanlı bir metin biçimidir. Her satır `alan: değer` yazar, boş satır olayı
tamamlar. Dört alan tanımlıdır: `event` olayın adını, `data` gövdesini, `id` sıra
kimliğini, `retry` yeniden bağlanma aralığını verir. İki nokta ile başlayan satır
yorumdur ve yok sayılır — bağlantıyı canlı tutan kalp atışı böyle gönderilir.

Aşağıdaki dosya hem akış üreten bir sunucu kurar hem de çözümleyiciyi ona karşı
çalıştırır. Bağlantı noktasını işletim sistemi seçer; sayı çalıştıran makineye göre
değişir ve çıktıda görünmez.

```js
// olay-akisi.mjs — text/event-stream ureten yerel sunucu + akis cozumleyici
import http from "node:http";

// --- Sunucu: son kimlikten sonrasini yayimlar -------------------------------
const OLCUMLER = [
  { kimlik: 1, kod: "KY-01", deger: -4.2 },
  { kimlik: 2, kod: "KY-02", deger: -6.1 },
  { kimlik: 3, kod: "KY-01", deger: -4.4 },
  { kimlik: 4, kod: "KY-02", deger: -5.9 },
];

const sunucu = http.createServer((istek, yanit) => {
  const sonKimlik = Number(istek.headers["last-event-id"] ?? 0);
  yanit.writeHead(200, {
    "content-type": "text/event-stream",
    "cache-control": "no-store",
    connection: "keep-alive",
  });
  yanit.write("retry: 3000\n\n");            // yeniden baglanma araligi onerisi
  yanit.write(": akış açık\n\n");            // yorum satiri: kalp atisi
  for (const o of OLCUMLER.filter((o) => o.kimlik > sonKimlik)) {
    yanit.write(`id: ${o.kimlik}\n`);
    yanit.write("event: olcum\n");
    yanit.write(`data: ${JSON.stringify(o)}\n\n`);
  }
  yanit.end();
});
await new Promise((coz) => sunucu.listen(0, "127.0.0.1", coz));
const TABAN = `http://127.0.0.1:${sunucu.address().port}`;

// --- Akis cozumleyici: alan satirlarindan olay uretir -----------------------
function cozumleyici(uygula) {
  let tampon = "";
  let olay = { ad: "message", veri: [], kimlik: null, yenidenDeneme: null };
  const sifirla = () => { olay = { ad: "message", veri: [], kimlik: null, yenidenDeneme: null }; };

  return function besle(parca) {
    tampon += parca;
    let kesme;
    while ((kesme = tampon.indexOf("\n")) !== -1) {
      const satir = tampon.slice(0, kesme);
      tampon = tampon.slice(kesme + 1);

      if (satir === "") {                        // bos satir: olayi gonder
        if (olay.veri.length > 0) uygula({ ...olay, veri: olay.veri.join("\n") });
        else if (olay.yenidenDeneme !== null) uygula({ ...olay, veri: null });
        sifirla();
        continue;
      }
      if (satir.startsWith(":")) continue;       // yorum satiri yok sayilir

      const ikiNokta = satir.indexOf(":");
      const alan = ikiNokta === -1 ? satir : satir.slice(0, ikiNokta);
      let deger = ikiNokta === -1 ? "" : satir.slice(ikiNokta + 1);
      if (deger.startsWith(" ")) deger = deger.slice(1);

      if (alan === "event") olay.ad = deger;
      else if (alan === "data") olay.veri.push(deger);
      else if (alan === "id" && !deger.includes("\0")) olay.kimlik = deger;
      else if (alan === "retry" && /^\d+$/.test(deger)) olay.yenidenDeneme = Number(deger);
    }
  };
}

// --- Tuketici ---------------------------------------------------------------
async function akisiTuket(sonKimlik, enCok) {
  const alinan = [];
  const denetleyici = new AbortController();
  let dur = false;
  const besle = cozumleyici((olay) => {
    if (dur) return;                       // yeterince alindi: kalani cozumleme
    alinan.push(olay);
    if (alinan.filter((o) => o.veri !== null).length >= enCok) {
      dur = true;
      denetleyici.abort("yeter");
    }
  });

  const yanit = await fetch(`${TABAN}/akis`, {
    headers: sonKimlik ? { "last-event-id": String(sonKimlik) } : {},
    signal: denetleyici.signal,
  });
  const cozucu = new TextDecoder();
  try {
    for await (const yigin of yanit.body) besle(cozucu.decode(yigin, { stream: true }));
  } catch {
    if (!denetleyici.signal.aborted) throw new Error("akış koptu");
  }
  return alinan;
}

const yaz = (o) => console.log(
  `  ad=${o.ad.padEnd(7)} kimlik=${String(o.kimlik).padEnd(4)}` +
  ` yenidenDeneme=${String(o.yenidenDeneme).padEnd(4)} veri=${o.veri}`);

console.log("ilk bağlantı (ilk iki olaydan sonra iptal):");
const birinci = await akisiTuket(0, 2);
birinci.forEach(yaz);

const sonAlinan = birinci.filter((o) => o.kimlik !== null).at(-1).kimlik;
console.log(`son alınan kimlik: ${sonAlinan}`);

console.log("yeniden bağlantı (Last-Event-ID ile):");
const ikinci = await akisiTuket(sonAlinan, 99);
ikinci.forEach(yaz);

// --- Olaylarin depoya islenmesi: tekrar eden kimlik yok sayilir -------------
const depo = new Map();
const gorulen = new Set();
let yoksayilan = 0;
for (const olay of [...birinci, ...ikinci, ...birinci]) {  // ilk parti bilerek tekrar
  if (olay.veri === null || olay.ad !== "olcum") continue;
  if (gorulen.has(olay.kimlik)) { yoksayilan++; continue; }
  gorulen.add(olay.kimlik);
  const o = JSON.parse(olay.veri);
  depo.set(o.kod, o);
}
console.log("işlenen olay:", gorulen.size, "| yok sayılan tekrar:", yoksayilan);
console.log("son durum   :", JSON.stringify([...depo.values()]));

sunucu.close();
```

```
ilk bağlantı (ilk iki olaydan sonra iptal):
  ad=message kimlik=null yenidenDeneme=3000 veri=null
  ad=olcum   kimlik=1    yenidenDeneme=null veri={"kimlik":1,"kod":"KY-01","deger":-4.2}
  ad=olcum   kimlik=2    yenidenDeneme=null veri={"kimlik":2,"kod":"KY-02","deger":-6.1}
son alınan kimlik: 2
yeniden bağlantı (Last-Event-ID ile):
  ad=message kimlik=null yenidenDeneme=3000 veri=null
  ad=olcum   kimlik=3    yenidenDeneme=null veri={"kimlik":3,"kod":"KY-01","deger":-4.4}
  ad=olcum   kimlik=4    yenidenDeneme=null veri={"kimlik":4,"kod":"KY-02","deger":-5.9}
işlenen olay: 4 | yok sayılan tekrar: 2
son durum   : [{"kimlik":3,"kod":"KY-01","deger":-4.4},{"kimlik":4,"kod":"KY-02","deger":-5.9}]
```

## Kopmayı Kabul Etmek

Uzun süren bağlantı kopar. Kablosuz ağ değişir, aradaki vekil sunucu boşta kalan bağlantıyı
kapatır, dizüstü bilgisayar uyanır. Bu bir hata değil, akış tabanlı çalışmanın normal
durumudur; tasarım kopmayı önlemeye değil, kopmadan sonra doğru yerden devam etmeye
yönelir.

Devamı sağlayan alan `id`'dir. İstemci aldığı son kimliği saklar, yeniden bağlanırken
`Last-Event-ID` başlığıyla gönderir, sunucu o kimlikten sonrasını yayımlar. Çıktıda bu
akış görünüyor: ilk bağlantı iki olaydan sonra kesildi, ikinci bağlantı üç ve dörtten
başladı. Bu düzenek çalışsın diye sunucunun **yayımladığı olayları bir süre saklaması**
gerekir; saklamıyorsa kopma aralığındaki olaylar kaybolur ve istemcinin yeniden bağlandıktan
sonra tam durumu bir kez daha çekmesi gerekir.

`retry` alanı yeniden bağlanma aralığını milisaniye olarak önerir. Kendi çözümleyicisini
yazan kod bu değeri dikkate almalı ve sabit bir aralık yerine artan bir bekleme
uygulamalıdır; sunucu çöktüğünde bütün istemcilerin aynı aralıkla dönmesi, ayağa kalkmaya
çalışan sunucuyu yeniden düşürür. Bu hesabın kendisi bir sonraki dersin konusudur.

Son iki satır üçüncü kuralı gösteriyor: **aynı olay iki kez gelebilir**. Yeniden bağlanma
sırasında sunucu son kimliği kapsayıcı yorumlayabilir, ya da istemci kaydettiği kimliği
yazamadan kopabilir. İşleme kodu bu yüzden **değişmez** olmalıdır: aynı olayı iki kez
uygulamak, bir kez uygulamakla aynı sonucu vermelidir. Örnekte bu, görülen kimliklerin
kümede tutulmasıyla sağlanıyor; ilk partinin tekrarı yok sayıldı ve son durum değişmedi.

Değişmezliğin ikinci yolu, olayı bir komut değil bir **duyuru** olarak yazmaktır. "Ölçüm
sayacını bir artır" iki kez uygulandığında bozulur; "KY-01 istasyonunun son ölçümü
`o-114`, değeri −4.4" iki kez uygulandığında aynı sonucu verir. Akış üzerinden taşınan
olaylar mümkün olduğunca ikinci biçimde tasarlanır.

## Çift Yönlü Soketin Farkları

Soket, sıradan bir HTTP isteğiyle başlar ve sunucu kabul ederse bağlantı protokol
değiştirir. Bu andan sonra HTTP semantiği — yöntem, durum kodu, başlıklar — geçerli
değildir; kanal üzerinde metin ya da ikili iletiler taşınır. Uygulamanın kendi ileti
biçimini tanımlaması gerekir; hangi alanın ileti türünü söylediği, sıra numarasının nerede
durduğu istemci ile sunucunun anlaşmasına kalmıştır.

Dört noktada olay akışından ayrılır. **Yeniden bağlanma yerleşik değildir**; kopmayı
saptamak ve yeniden kurmak uygulamanın işidir. **Kopma her zaman fark edilmez**; iletim
sessizce kesildiğinde bağlantı açık görünmeye devam edebilir, bu yüzden iki taraf
düzenli aralıklarla yoklama iletisi gönderip yanıt bekler ve yanıt gelmezse bağlantıyı
ölü sayar. **Sıraya alma sorumluluğu istemcidedir**: bağlantı kopukken gönderilmek istenen
iletiler bir kuyrukta bekletilir, bağlantı kurulunca boşaltılır — ya da bilinçli olarak
atılır. **Kimlik doğrulaması ilk el sıkışmada yapılır**; kanal açıldıktan sonra
başlık gönderilemez, bu yüzden oturumun kanal ömrü boyunca geçerliliği ayrı bir sorundur
ve Oturum Yenileme dersinde ele alınır.

## Akışın Duruma İşlenmesi

Gelen olayın tek başına bir anlamı yoktur; anlamı, üzerine uygulandığı duruma göre oluşur.
Üç sorun her uygulamada karşımıza çıkar.

**Başlangıç durumu ile akışın buluşması.** Ekran açıldığında önce tam liste çekilir, sonra
akış dinlenir. İki işlem arasında geçen sürede üretilen olaylar kaçar. Doğru sıra
tersidir: önce akış açılır ve gelen olaylar tampona alınır, sonra tam liste çekilir,
listenin damgasından sonraki tampon olayları uygulanır, tampon boşaltılır.

**Sıra dışı gelme.** Olaylar farklı yollardan gelirse — biri akıştan, biri kullanıcının
kendi işleminin yanıtından — daha eski bir değer daha yeninin üstüne yazılabilir. Çözüm,
her kaydın bir sürüm ya da zaman damgası taşıması ve işleme kodunun **yalnız daha yeni
damgayı** kabul etmesidir.

**Görünmeyen sekme.** Arka plandaki bir sekmede akışı açık tutmak, hem sunucuda bağlantı
tüketir hem istemcide iş üretir. Görünürlük değiştiğinde akış kapatılır, sekme öne
geldiğinde son kimlikten devam edilerek yeniden açılır. Bu, aynı zamanda kaçırılan
olayların telafi yolunu kullanmanın doğal bir örneğidir.

## Özet

- Bağlantıyı her zaman istemci kurar; taşımaların farkı bağlantının süresinde ve yön
  sayısındadır. Yoklama en ucuz kurulumdur, gecikmesi aralığı kadardır.
- Olay akışı tek yönlü ve sıradan bir HTTP yanıtıdır; soket çift yönlüdür ve protokol
  değiştirerek kurulur, karşılığında yeniden bağlanma ile canlılık denetimini uygulamaya
  bırakır.
- Akış biçimi satır tabanlıdır: `event`, `data`, `id` ve `retry` alanları, boş satırla
  tamamlanan olaylar, yorum satırıyla gönderilen kalp atışı.
- Kopma normaldir; son olay kimliği saklanır ve yeniden bağlanmada `Last-Event-ID` ile
  bildirilir. Sunucu yayımladığı olayları bir süre saklamıyorsa devam mümkün olmaz.
- Aynı olay iki kez gelebilir; işleme kodu değişmez olmalı, olaylar artırım komutu değil
  son durum duyurusu biçiminde tasarlanmalıdır.
- Başlangıç listesi ile akış buluşturulurken önce akış açılıp olaylar tamponlanır; sıra
  dışı gelen olaylar sürüm damgasıyla elenir.

## Sonraki Adım

Son üç ders veriyi almanın yollarını kurdu; hepsinin ortak noktası, verinin bir süre
**henüz gelmemiş** olmasıdır. Ekran o sırada bir şey göstermek zorunda: boş bir çerçeve,
bir yükleme göstergesi, eski veri, ya da hata. Bu durumları ayrı ayrı mantıksal
bayraklarla tutmak — yükleniyor, hata var, veri var — kısa sürede birbiriyle çelişen
kombinasyonlar üretir. Sonraki ders görünümün hangi durumlarda bulunabileceğini sayılı bir
kümeye indirger ve geçişleri bir tabloyla sınar.
