---
title: 'Akış Yanıtları'
source: 'https://academia.sh/tr/kurslar/eszamansiz-isleme/akis-yanitlari'
course: 'Önbellekleme, Kuyruklar ve Eşzamansız İşleme'
language: tr
updated: '2026-08-17T18:06:48+00:00'
license: 'CC BY-SA 4.0'
---

# Akış Yanıtları

Yanıtın kendisinin parça parça üretilmesi: parçalı aktarımın çerçevelemesi, ilk bayta kadar geçen sürenin ölçülmesi, başlıklar gönderildikten sonra hata bildirmenin sınırı ve akış yanıtında geri basınç.

Bu konudaki dört yaklaşım da sunucudan istemciye **olay** taşıdı: durum değişti, sıra
ilerledi, stok güncellendi. Geriye tek bir durum kalıyor. Bazen taşınacak şey bir olay
değil, **yanıtın kendisidir**.

Otuz bin ödünç kaydını tarayan aylık rapor, sonuç tamamlanmadan da satır satır
gönderilebilir. Bu, yeni bir protokol gerektirmez; HTTP yanıt gövdesinin parça parça
yazılmasıyla yapılır ve karşılığında istemcinin ilk veriyi görmesi için beklediği süre
belirgin ölçüde kısalır.

## İki Biçim

Bir yanıt gövdesi iki biçimde gönderilebilir. Uzunluğu biliniyorsa `Content-Length`
başlığında bildirilir ve alıcı kaç bayt bekleyeceğini baştan bilir. Uzunluğu bilinmiyorsa
**parçalı aktarım** (chunked transfer encoding) kullanılır: gövde, her biri kendi
uzunluğunu taşıyan parçalara bölünür ve son parça sıfır uzunlukla akışın bittiğini
bildirir.

Aşağıdaki sunucu aynı raporu üç yolla veriyor: tamamını biriktirip gönderen `/tam`,
her satırı ürettiği anda yazan `/akan` ve üretimi yarıda bırakan `/kesik`.

```js
// parcali.mjs — aylik odunc raporunu iki bicimde sunar
//   /tam    tum satirlar bellekte toplanir, tek parca halinde gonderilir
//   /akan   her satir uretildigi anda yazilir: parcali aktarim
//   /kesik  ucuncu satirdan sonra uretim basarisiz olur
import { createServer } from "node:http";

const SATIR = 10;
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const satirUret = (i) =>
  `${String(i).padStart(2, "0")};sube-${1 + (i % 3)};odunc=${40 + i * 3}\n`;

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

  if (yol === "/tam") {                            // uzunluk biliniyor: tek parca
    const parcalar = [];
    for (let i = 1; i <= SATIR; i++) { await bekle(100); parcalar.push(satirUret(i)); }
    const govde = parcalar.join("");
    yanit.writeHead(200, { "content-type": "text/csv; charset=utf-8",
                           "content-length": Buffer.byteLength(govde) });
    return yanit.end(govde);
  }

  if (yol === "/akan" || yol === "/kesik") {       // uzunluk bilinmiyor: parcali aktarim
    yanit.writeHead(200, { "content-type": "text/csv; charset=utf-8" });
    for (let i = 1; i <= SATIR; i++) {
      await bekle(100);
      if (yanit.writableEnded) return;             // istemci cekildi
      if (yol === "/kesik" && i === 4) {
        // Basliklar coktan gonderildi: durum kodu artik degistirilemez.
        yanit.end("#hata;rapor yarida kesildi;satir=3\n");
        return;
      }
      if (!yanit.write(satirUret(i))) await new Promise((c) => yanit.once("drain", c));
    }
    return yanit.end();
  }
  yanit.writeHead(404).end();
}).listen(8401, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8401"));
```

Parçalı aktarımı açıkça istemek gerekmez. `Content-Length` yazılmadığında ve gövde tek
çağrıda bitirilmediğinde çalışma zamanı çerçevelemeyi kendisi yapar. Yazılan tek şey
satırın kendisidir.

Akan yanıtın her yazmadan önce `writableEnded` durumunu sınaması, sunucu gönderimli
olaylardaki kuralın aynısıdır: istemci çekildiğinde üretimi sürdürmek boşa harcanan
işlemci ve kapalı yuvaya yazma hatası demektir.

## Ölçüm

Aşağıdaki betik iki biçimi başlık, gecikme ve parça varış anları açısından karşılaştırıyor.
Ölçümü yapan `zamanlayici.mjs` her parçanın geliş anını yüz milisaniyelik dilimlerle
yazıyor.

```js
// zamanlayici.mjs — akan yanitin her parcasinin varis anini 100 ms dilim olarak yazar
const basla = performance.now();
let yanit;
try {
  yanit = await fetch("http://127.0.0.1:8401/akan");
} catch {
  console.log("sunucuya baglanilamadi");
  process.exit(0);
}
for await (const parca of yanit.body) {
  const dilim = Math.round((performance.now() - basla) / 100);
  process.stdout.write(`${String(dilim).padStart(2)} dilim  ${Buffer.from(parca)}`);
}
```

Bağlantı noktası 8401 keyfîdir ve boş olmalıdır.

```bash
#!/usr/bin/env bash
# parcali.mjs'yi baslatir; basliklari, gecikmeleri ve parca varislerini olcer.
node parcali.mjs > /dev/null & sunucu=$!
sleep 1
A=http://127.0.0.1:8401

echo "--- yanit basliklari ---"
for yol in tam akan; do
  printf '%-5s ' "$yol"
  curl -sS -o /dev/null -D - "$A/$yol" | grep -i -E 'content-length|transfer-encoding' | tr -d '\r' | paste -sd' ' -
done

echo "--- ilk bayt ve toplam sure (100 ms dilim) ---"
for yol in tam akan; do
  curl -sS -o /dev/null -w "$yol %{time_starttransfer} %{time_total}\n" "$A/$yol"
done | awk '{ printf "%-5s ilk-bayt=%2d dilim  toplam=%2d dilim\n", $1, int($2*10), int($3*10) }'

echo "--- akan yanitin parcalarinin varis anlari ---"
node zamanlayici.mjs

echo "--- ham parcali cerceveleme (ilk uc parca) ---"
curl -sS --raw -N "$A/akan" 2>/dev/null | head -c 60 | od -c | head -4

echo "--- uretim yarida kesilirse ---"
curl -sS -w 'durum=%{http_code}\n' "$A/kesik"
kill "$sunucu"
```

```
--- yanit basliklari ---
tam   content-length: 190
akan  Transfer-Encoding: chunked
--- ilk bayt ve toplam sure (100 ms dilim) ---
tam   ilk-bayt=10 dilim  toplam=10 dilim
akan  ilk-bayt= 1 dilim  toplam=10 dilim
--- akan yanitin parcalarinin varis anlari ---
 1 dilim  01;sube-2;odunc=43
 2 dilim  02;sube-3;odunc=46
 3 dilim  03;sube-1;odunc=49
 4 dilim  04;sube-2;odunc=52
 5 dilim  05;sube-3;odunc=55
 6 dilim  06;sube-1;odunc=58
 7 dilim  07;sube-2;odunc=61
 8 dilim  08;sube-3;odunc=64
 9 dilim  09;sube-1;odunc=67
10 dilim  10;sube-2;odunc=70
--- ham parcali cerceveleme (ilk uc parca) ---
0000000    1   3  \r  \n   0   1   ;   s   u   b   e   -   2   ;   o   d
0000020    u   n   c   =   4   3  \n  \r  \n   1   3  \r  \n   0   2   ;
0000040    s   u   b   e   -   3   ;   o   d   u   n   c   =   4   6  \n
0000060   \r  \n   1   3  \r  \n   0   3   ;   s   u   b
--- uretim yarida kesilirse ---
01;sube-2;odunc=43
02;sube-3;odunc=46
03;sube-1;odunc=49
#hata;rapor yarida kesildi;satir=3
durum=200
```

Üç sonuç okunuyor.

**Başlıklar** biçimi ele veriyor: `/tam` uzunluğu bildiriyor, `/akan` parçalı aktarım
kullanıyor. İkisi bir arada bulunamaz.

**Toplam süre iki biçimde de aynı**: on dilim. Akıtmak işi hızlandırmaz, çünkü rapor yine
aynı sürede üretiliyor. Değişen tek şey **ilk bayta kadar geçen süredir**: on dilimden bir
dilime iniyor. Kullanıcı raporun ilk satırlarını, tamamının bitmesini beklemeden görüyor.
Bu, gerçek zamanlı veri konusunun tamamındaki ortak kazancın burada aldığı biçimdir.

**Parça varışları** ilk sonucu doğruluyor: satırlar birer dilim arayla geliyor, hepsi
sonda toplu olarak değil.

## Çerçeveleme

Ham çıktı parçalı aktarımın biçimini gösteriyor. Her parça, uzunluğunu **onaltılık**
olarak yazan bir satırla başlıyor: `13` onaltılık, yani on dokuz bayt — bir rapor satırının
uzunluğu. Ardından `\r\n`, sonra verinin kendisi, sonra yine `\r\n` geliyor.

Bu çerçeveleme, uzunluk baştan bilinmediğinde alıcının gövdenin nerede bittiğini nasıl
anlayacağı sorusunu çözer: son parça sıfır uzunlukla yazılır ve akış orada biter.
Uzunluk bildirilmeden gövdeyi bağlantıyı kapatarak bitirmek de teknik olarak mümkündür,
ama o zaman alıcı yanıtın tamamlanmış mı yoksa kesilmiş mi olduğunu ayırt edemez.

## Başlıklar Gönderildikten Sonra

Son ölçüm bölümü akış yanıtlarının en önemli sınırını gösteriyor. `/kesik` ucunda rapor
üretimi dördüncü satırda başarısız oluyor, ama durum kodu **200** kalıyor. Çünkü başlıklar
ilk parçayla birlikte çoktan gönderildi ve HTTP'de başlıklar geri alınamaz.

Bunun üç sonucu vardır. Hata artık gövdenin **içinde** bildirilmek zorundadır; çıktı
biçiminin bunun için bir yeri olmalıdır — burada `#` ile başlayan bir bildirim satırı.
İkincisi, alıcı tarafın bu satırı okuması gerekir; okumazsa eksik raporu tam sanır.
Üçüncüsü, tamamlandığını gösteren bir **bitiş imi** yazmak, hata imi yazmaktan daha
güvenlidir: bağlantı kopması durumunda hata satırı da yazılamaz, ama bitiş iminin
yokluğu her durumda fark edilir.

HTTP bunun için ardıl başlıklar (trailer) da tanımlar; gövdenin ardından başlık
gönderilmesine izin verirler. Uygulamada ara katman desteği düzensiz olduğu için gövde
içi im daha yaygın ve daha güvenli bir seçimdir.

## Akış Yanıtında Geri Basınç

Koddaki `yanit.write(...)` dönüşünün sınanması, geri basınç dersinde ölçülen kuralın
buradaki karşılığıdır. Ağ istemcisi yavaşsa yazılan satırlar sunucunun tamponunda birikir;
dönüş değeri yok sayılırsa tampon sınırsız büyür.

Rapor on satırlık olduğunda bu görünmez. Otuz bin satırlık bir raporu yavaş bir bağlantıya
akıtan sunucu ise, dönüşü dinlemediğinde tüm raporu bellekte tutmuş olur — yani akıtmanın
tek gerekçesi olan bellek kazancını da kaybeder. Akış yanıtının değeri, üretim hızının
tüketim hızına **bağlanmasındadır**.

## Ne Zaman Akıtmalı

Akıtmak her yanıt için doğru değildir. Kazanç, üretim süresi uzun ve sonucun ilk parçası
tek başına anlamlıysa gerçekleşir: rapor satırları, arama sonuçları, kütük kayıtları.
Küçük ve tek parça anlamlı olan yanıtlarda — bir ödünç kaydının ayrıntısı — akıtmak yalnız
çerçeveleme gideri ekler.

İki pratik engel vardır. Ara katmanlar yanıtı tamponlayabilir ve akışı tek parça hâline
getirebilir; bu durumda ölçülen ilk bayt süresi kazanç göstermez. Ve sıkıştırma, tampon
dolmadan çıktı üretmediği için akıtmayı geciktirebilir; akış yanıtlarında sıkıştırmanın
parça sınırlarında boşaltılacak biçimde ayarlanması gerekir.

## Özet

- Uzunluğu bilinmeyen yanıt gövdesi parçalı aktarımla gönderilir; her parça uzunluğunu onaltılık olarak yazar ve sıfır uzunluklu parça akışı bitirir.
- Akıtmak toplam süreyi kısaltmaz; ölçümde iki biçim de on dilim sürdü, ama ilk bayta kadar geçen süre on dilimden bir dilime indi.
- Başlıklar ilk parçayla gönderildiği için durum kodu sonradan değiştirilemez; hata gövde içinde bildirilir ve bitiş imi yazmak hata imi yazmaktan güvenlidir.
- Akış yanıtında yazma dönüşü dinlenmezse gövdenin tamamı sunucu tamponunda birikir ve akıtmanın bellek kazancı ortadan kalkar.
- Akıtmanın kazancı uzun üretim süresi ile ilk parçanın tek başına anlamlı olmasına bağlıdır; ara katman tamponlaması ve sıkıştırma bu kazancı görünmez kılabilir.

## Kurs Kapanışı

Bu kurs, isteğin yanıtlanma yolunu kısaltmanın ve o yolun dışına taşınan işi güvenilir
kılmanın dört ayrı katmanını kurdu.

**Önbellekleme** hesaplanmış sonucun yeniden kullanılmasını ele aldı: istemci, kenar,
sunucu ve veritabanı katmanları; yanında okuma, içinden yazma ve arkadan yazma
stratejileri; süre, olay ve sürüm tabanlı geçersizleştirme; ad alanı ve kiracı ayrımı olan
anahtar tasarımı; sıcak anahtar ile sürü etkisinin önlenmesi; HTTP doğrulayıcıları ve
önbellek denetim yönergeleri; ve isabet oranının ölçümü. Ortak dersi şuydu: önbellek bir
hızlandırıcı değil, bayatlığın denetimli kabulüdür.

**Mesajlaşma** işin istek yolundan çıkarılmasını kurdu: eşzamansız işlemenin gerekçesi,
üretici–tüketici kuyruğu, yayımla–abone ol, akış ile kuyruk arasındaki fark, en çok bir
kez ile en az bir kez ve tam bir kez teslim semantiği, sıralama ve bölümlemenin maliyeti,
ölü mektup kuyruğu, zehirli mesaj ve geri çekilme. Ortak dersi teslim garantisinin
bedava olmadığıydı: her güvence sıralamadan, hacimden ya da gecikmeden ödün ister.

**Arka Plan İşleri** mesajı işleyen tarafı bir ölçekleme birimi olarak ele aldı: işçi
süreçleri ve gözetim, yineleyen görev tanımı ile çakışmayı önleyen kira kilidi, uzun
süren işte ilerleme bildirimi ve işbirlikçi iptal, üretim hızının tüketim hızını aştığı
yerde geri basınç, ve aşırı yükte denetimli reddetmeyi sağlayan yük boşaltma. Ortak dersi
şuydu: hız uyuşmazlığı yok edilemez, yalnız nereye yazılacağı seçilebilir — belleğe,
kayba ya da üreticinin hızına.

**Gerçek Zamanlı Veri** sonucun istemciye ulaşmasını kurdu: kısa ve uzun yoklamanın istek
maliyeti, sunucu gönderimli olaylar ve olay kimliğiyle yeniden bağlanma, kendi el
sıkışmasıyla kurulan WebSocket bağlantısının yaşam döngüsü, çok örnekli kurulumda kanal
dağıtımı ve parça parça üretilen akış yanıtları. Ortak dersi açık bağlantının bir durum
olduğuydu: taşıdığı gecikme kazancının bedeli, o durumun yönetilmesidir.

Kursun boyunca sessizce korunan bir varsayım vardı. Önbellek, kuyruk, işçi ve akış hep
**tek bir uygulamanın** parçaları gibi ele alındı; hepsi aynı dağıtım biriminin içinde,
aynı kod tabanında, aynı ekibin sorumluluğundaydı. Bu varsayım kavramları öğrenmek için
gerekliydi ama gerçek sistemlerde uzun sürmez.

Sonraki kurs, **Servis Mimarileri**, tam olarak bu birimi bölmeyi ele alır: tek parça,
modüler ve dağıtık mimariler arasında ölçüte dayalı seçim yapmayı, servis sınırlarını alan
modeline göre çizmeyi ve servisler arası iletişim ile tutarlılık kalıplarını uygulamayı.
Bu kursta ölçülen kavramların hiçbiri orada geçersizleşmez; hepsi bir sınırın karşısına
geçer ve orada daha pahalı hâle gelir. Teslim garantisi artık bir kuyruğun değil iki
ekibin sözleşmesidir. Tekillik anahtarı, ağ üzerinden gelen tekrarları da karşılamak
zorundadır. Geri basınç bir süreç içinde değil, servisler arasında yayılır. Kanal
dağıtımı, örnekler arasında değil sahiplikleri farklı servisler arasında yapılır. Bu
kursta ölçülen her sayı, orada aynı soruların daha büyük ölçekteki karşılığını okumanın
aracı olur.
