---
title: 'Mesaj Kuyrukları ve Akışlar'
source: 'https://academia.sh/tr/kurslar/mimari-bicemler/mesaj-kuyruklari-ve-akislar'
course: 'Mimari Biçemler'
language: tr
updated: '2026-08-17T18:10:36+00:00'
license: 'CC BY-SA 4.0'
---

# Mesaj Kuyrukları ve Akışlar

Yayımcı ile tüketici arasına bir depo konmasının ölçülmesi: iletiyi bıraktığı anda koşmuş tüketici iş birimi, tüketici üreticiden yavaş kaldığında bekleyen ileti sayısı, tek ve iki tüketicide sıranın korunup korunmadığı, bir ileti ikinci kez teslim edildiğinde açılan teslimat kaydı ve sonradan eklenen bir birimin kuyruk ile akışta okuyabildiği geçmiş kayıt sayısı.

Önceki dersin veriyolunun ölçülmemiş bir özelliği vardı: `yayimla` çağrısı, bağlı bütün
işleyiciler bitmeden geri dönmüyordu. Dört işleyici aynı çağrı yığıtında sırayla koşuyor,
dolayısıyla ücreti kesinleştiren modülün işi en yavaş işleyicinin işi kadar sürüyordu. Arşiv
yazımı yavaşlarsa ücret kesinleştirme de yavaşlar; oysa arşivin ne zaman yazıldığı ücretlendirme
için önemli değildir. Bu ders araya bir **depo** koyar: yayımcı iletiyi bırakır ve döner, tüketici
onu sonra alır.

**Mesaj kuyruğu** (message queue) ile **akış** (stream) Önbellekleme, Kuyruklar ve Eşzamansız
İşleme kursunun Mesajlaşma konusunda kurulmuş ve ölçülmüştü: teslim anlamları, sıralama ile
bölümleme arasındaki ödünleşim, ölü mektup kuyruğu ve yeniden deneme oraya aittir ve burada
yeniden ele alınmaz. Buraya düşen kısım biçemin kendisidir — deponun neyi garanti ettiği, neyi
etmediği ve bunun mimariye getirdiği kısıt. Ölçülecek dört şey vardır: bırakma anında koşmuş
tüketici işi, bekleyen ileti sayısı, sıranın korunup korunmadığı ve aynı iletinin iki kez
işlenmesinin sonucu.

Bağlandığı **kalite niteliği** güvenilirliktir ve nitelik sorusu şudur: tüketici üreticiden yavaş
kaldığında ya da bir ileti ikinci kez teslim edildiğinde kaç iş birikir ve kaç teslimat kaydı iki
kez açılır. İkinci bir ölçü başarım verimliliği ailesine girer: yayımcı iletiyi bıraktığında kaç
tüketici iş birimi koşmuş olur.

## İki Uç ve Bir Depo

Tüketici teslimat operasyonu bağlamındadır: gelen her ileti için teslimat kaydı açar. Her iletinin
üç iş birimi tuttuğu varsayılır. **Bu bir modeldir, ölçüm değildir**; sayılan şey iş birimi
adedidir, hiçbir süre ölçülmemiştir. `anahtarli` seçeneği aynı gönderi kodunu ikinci kez
gördüğünde kayıt açmaz; dersin sonunda bu seçeneğin ölçüsü alınır.

```sh
mkdir -p esz
```

```js
// esz/teslimat.mjs — tuketici: her ileti icin teslimat kaydi acar ve 3 is birimi harcar
export const IS_BIRIMI = 3;

export function teslimatci(anahtarli = false) {
  const kayit = [], gorulen = new Set();
  let birim = 0;
  return {
    isle(ileti) {
      birim += IS_BIRIMI;
      if (anahtarli && gorulen.has(ileti.kod)) return;
      gorulen.add(ileti.kod);
      kayit.push(ileti.kod);
    },
    birim: () => birim,
    kayit: () => kayit,
  };
}
```

Depo iki işlem açar. `koy` iletiyi sona ekler ve hiçbir tüketici çağırmaz; `al` en eski iletiyi
verir. Bu iki imzanın arasındaki boşluk biçemin tamamıdır: koyan ile alan aynı anda çalışmak
zorunda değildir.

```js
// esz/kuyruk.mjs — mesaj kuyrugu: koyan beklemez, alan sirayla alir
export function kuyruk() {
  const bekleyen = [];
  let konan = 0, alinan = 0, tepe = 0;
  return {
    koy(ileti) { bekleyen.push(ileti); konan += 1; tepe = Math.max(tepe, bekleyen.length); },
    al() { if (bekleyen.length === 0) return null; alinan += 1; return bekleyen.shift(); },
    uzunluk: () => bekleyen.length,
    sayac: () => ({ konan, alinan, tepe }),
  };
}
```

## Yayımcı Beklemiyor

İlk betik yirmi iletiyi iki düzende geçirir. Eşzamanlı düzende yayımcı tüketiciyi doğrudan çağırır;
kuyruk düzeninde iletiyi bırakır. Bırakma bittiği anda koşmuş tüketici işi sayılır, sonra kuyruk
boşaltılır ve iki düzenin aynı kaydı ürettiği doğrulanır. İkinci bölümde üretici tur başına beş,
tüketici tur başına üç ileti işler ve üretim dördüncü turdan sonra durur.

```js
// bloke.mjs — yayimci olayi biraktiginda kosmus tuketici is birimi ve kuyrukta bekleyen ileti
import { teslimatci, IS_BIRIMI } from "./esz/teslimat.mjs";
import { kuyruk } from "./esz/kuyruk.mjs";

const ILETI = Array.from({ length: 20 }, (_, i) => ({ kod: `G-${String(i + 1).padStart(2, "0")}` }));

const hemen = teslimatci();
for (const ileti of ILETI) hemen.isle(ileti);

const k = kuyruk(), sonra = teslimatci();
for (const ileti of ILETI) k.koy(ileti);
const birakmaAninda = { birim: sonra.birim(), bekleyen: k.uzunluk() };
let alinan;
while ((alinan = k.al()) !== null) sonra.isle(alinan);

const GENISLIK = [15, 25, 16, 18];
const SATIR = [
  ["eszamanli", ILETI.length, hemen.birim(), 0, hemen.birim()],
  ["kuyruk", ILETI.length, birakmaAninda.birim, birakmaAninda.bekleyen, sonra.birim()],
];
console.log("duzen      yayimci cagrisi  birakma aninda kosan is  bekleyen ileti  sonunda kosan is");
for (const [ad, ...s] of SATIR) console.log(ad.padEnd(11) + s.map((v, i) => String(v).padStart(GENISLIK[i])).join(""));
console.log(`kuyruk sayaci: ${JSON.stringify(k.sayac())}, kayit esit mi = ${JSON.stringify(hemen.kayit()) === JSON.stringify(sonra.kayit())}`);

const URETIM = [5, 5, 5, 5, 0, 0, 0, 0];
const TUKETIM = 3;
const k2 = kuyruk(), t2 = teslimatci();
let sira = 0;
console.log("\ntur  uretilen  tuketilen  bekleyen");
for (const [i, uret] of URETIM.entries()) {
  for (let n = 0; n < uret; n += 1) { sira += 1; k2.koy({ kod: `H-${sira}` }); }
  let tuketilen = 0;
  for (let n = 0; n < TUKETIM; n += 1) {
    const ileti = k2.al();
    if (ileti === null) break;
    t2.isle(ileti);
    tuketilen += 1;
  }
  console.log(`${String(i + 1).padStart(3)}${String(uret).padStart(10)}${String(tuketilen).padStart(11)}${String(k2.uzunluk()).padStart(10)}`);
}
console.log(`tepe bekleyen = ${k2.sayac().tepe}, is birimi = ${t2.birim()}, ileti basina is birimi = ${IS_BIRIMI}`);
```

```sh
node bloke.mjs
```

```
duzen      yayimci cagrisi  birakma aninda kosan is  bekleyen ileti  sonunda kosan is
eszamanli               20                       60               0                60
kuyruk                  20                        0              20                60
kuyruk sayaci: {"konan":20,"alinan":20,"tepe":20}, kayit esit mi = true

tur  uretilen  tuketilen  bekleyen
  1         5          3         2
  2         5          3         4
  3         5          3         6
  4         5          3         8
  5         0          3         5
  6         0          3         2
  7         0          2         0
  8         0          0         0
tepe bekleyen = 11, is birimi = 60, ileti basina is birimi = 3
```

## Sayıların Okunması

İlk tablonun son sütunu iki düzende de 60'tır: iş yok olmadı, aynı iş yapıldı ve iki düzen aynı
kaydı üretti. Değişen şey işin **ne zaman** yapıldığıdır. Eşzamanlı düzende yayımcı yirminci
çağrıyı bitirdiğinde altmış iş birimi onun zamanından gitmişti; kuyruk düzeninde aynı anda sıfır
iş birimi koşmuştu ve depoda yirmi ileti bekliyordu. Bekleyen ileti sayısı, bedelin adıdır:
yayımcı beklemekten kurtuldu, ama sistemde artık işlenmemiş iş duruyor ve bu iş bir yerde
tutulmak zorunda.

İkinci tablo bu birikimin nasıl davrandığını gösterir. Üretim tüketimden hızlıyken bekleyen ileti
her turda ikişer artıyor; dördüncü turun üretiminden sonra kuyruk en yüksek değerine, on bire
ulaşıyor. Üretim durduğunda kuyruk erimeye başlıyor ve üç tur sonra boşalıyor. Bu, biçemin
temel kısıtıdır: kuyruk hız farkını **saklar**, ortadan kaldırmaz. Üretim hızı tüketim hızının
üstünde kalıcı olarak durursa bekleyen ileti sayısı sınırsız büyür; kuyruğun sağladığı şey
farkın geçici olduğu durumda yayımcının beklememesidir.

## Sıra ve Tekrar

İkinci betik iki soru sorar. Birincisi sıradır: kuyruğa konma sırası ile işlenme sırası aynı mı?
Tek tüketicide ve aynı kuyruğun ucunda çalışan iki tüketicide ayrı ayrı ölçülür. İki tüketicili
düzende işçilerin iş birimi maliyeti farklıdır ve boşalan işçi sıradaki iletiyi alır; bitiş sırası
buna göre kurulur. **Bu bir modeldir, ölçüm değildir** — sayılan şey iş birimi ve bitiş sırasıdır,
hiçbir süre ölçülmemiştir. İkinci soru tekrardır: bir ileti onaylanmadığı için kuyruğa geri
konursa ne olur?

```js
// sira-tekrar.mjs — tek ve iki tuketicide sira, bir ileti yeniden teslim edildiginde acilan kayit
import { teslimatci } from "./esz/teslimat.mjs";
import { kuyruk } from "./esz/kuyruk.mjs";

const ILETI = Array.from({ length: 8 }, (_, i) => ({ kod: `G-${i + 1}` }));
const SIRA = ILETI.map((i) => i.kod);

const k = kuyruk(), tek = teslimatci();
for (const ileti of ILETI) k.koy(ileti);
let alinan;
while ((alinan = k.al()) !== null) tek.isle(alinan);

const tersine = (dizi) => {
  const yer = new Map(SIRA.map((kod, i) => [kod, i]));
  let t = 0;
  for (let a = 0; a < dizi.length; a += 1)
    for (let b = a + 1; b < dizi.length; b += 1) if (yer.get(dizi[a]) > yer.get(dizi[b])) t += 1;
  return t;
};

const MALIYET = [1, 4];
const bos = MALIYET.map(() => 0);
const bitis = [];
const k2 = kuyruk();
for (const ileti of ILETI) k2.koy(ileti);
while ((alinan = k2.al()) !== null) {
  const j = bos[0] <= bos[1] ? 0 : 1;
  bos[j] += MALIYET[j];
  bitis.push([alinan.kod, bos[j]]);
}
const bitisSirasi = [...bitis].sort((a, b) => a[1] - b[1]).map(([kod]) => kod);

console.log(`koyulan sira : ${SIRA.join(" ")}`);
console.log(`tek tuketici : ${tek.kayit().join(" ")}  tersine cift = ${tersine(tek.kayit())}`);
console.log(`iki tuketici : ${bitisSirasi.join(" ")}  tersine cift = ${tersine(bitisSirasi)}`);

for (const [ad, anahtarli] of [["anahtarsiz", false], ["anahtarli", true]]) {
  const k3 = kuyruk(), t3 = teslimatci(anahtarli);
  for (const ileti of ILETI) k3.koy(ileti);
  let sayi = 0;
  while ((alinan = k3.al()) !== null) {
    t3.isle(alinan);
    sayi += 1;
    if (sayi === 3) k3.koy(alinan);
  }
  const s = k3.sayac();
  console.log(`${ad.padEnd(11)} teslim = ${s.alinan}, farkli ileti = ${new Set(ILETI.map((i) => i.kod)).size}, acilan teslimat kaydi = ${t3.kayit().length}, is birimi = ${t3.birim()}`);
}
```

```sh
node sira-tekrar.mjs
```

```
koyulan sira : G-1 G-2 G-3 G-4 G-5 G-6 G-7 G-8
tek tuketici : G-1 G-2 G-3 G-4 G-5 G-6 G-7 G-8  tersine cift = 0
iki tuketici : G-1 G-3 G-4 G-2 G-5 G-6 G-8 G-7  tersine cift = 3
anahtarsiz  teslim = 9, farkli ileti = 8, acilan teslimat kaydi = 9, is birimi = 27
anahtarli   teslim = 9, farkli ileti = 8, acilan teslimat kaydi = 8, is birimi = 27
```

Tek tüketicide tersine çift sayısı sıfırdır: sıra korunmuştur ve bu, deponun `al` imzasının
sonucudur. İkinci tüketici eklendiğinde aynı kuyruk, aynı sırayla dağıtım yapmasına karşın bitiş
sırası üç yerde bozuldu. Sıranın korunması tek tüketiciye bağlı bir özelliktir; ikinci tüketici
eklemek bir başarım kararı değil, **sıra garantisinden vazgeçme** kararıdır. Mimari sonucu şudur:
sırası önemli olan işler ile paralel işlenebilen işler aynı kuyruktan geçemez.

Son iki satır tekrarın faturasıdır. Sekiz farklı ileti dokuz kez teslim edildi. Anahtarsız
tüketicide açılan teslimat kaydı dokuz oldu: aynı gönderi için iki kayıt açıldı. Anahtarlı
tüketicide sekiz kaldı, yani ikinci teslim etkisiz geçti — ama iş birimi iki düzende de 27'dir,
çünkü ileti yine alındı ve yine işlendi. Buradaki ölçüm biçemin kısıtını verir: depo iletiyi
sıraya koyar, **tekliği** garanti etmez; teklik tüketicinin kendi anahtarıyla sağlanmak
zorundadır. Aynı iletinin iki kez ulaşmasına karşı iki tüketici tasarımının nasıl saptığı bu
kursun Olay Güdümlü Mimari dersinde ayrıca ölçülür.

## Tüketmek ile Silmek

Kuyruğun `al` işlemi iletiyi depodan çıkarır: tüketmek silmektir. Akış bu iki işi ayırır — kayıt
yerinde kalır, her tüketici kendi **konumunu** tutar. Konum, saklama süresi ve yeniden okuma
Mesajlaşma konusundaki Akışlar ve Kuyruk Farkı dersinde ölçülmüştü. Buradaki soru mimaridir:
sonradan eklenen bir birim geçmişi kendi başına kurabilir mi?

```js
// esz/gunluk.mjs — akis: tuketmek silmez, her tuketici kendi konumunu tutar
export function gunluk() {
  const kayit = [];
  return { ekle: (ileti) => kayit.push(ileti), oku: (konum) => kayit.slice(konum), uzunluk: () => kayit.length };
}
```

```js
// gecmis.mjs — kuyruk tuketilince bosalir, akis konumla okunur: sonradan eklenen birimin gecmisi
import { kuyruk } from "./esz/kuyruk.mjs";
import { gunluk } from "./esz/gunluk.mjs";
import { teslimatci } from "./esz/teslimat.mjs";

const ILETI = Array.from({ length: 20 }, (_, i) => ({ kod: `G-${i + 1}` }));

const k = kuyruk(), kuyrukTuketicisi = teslimatci();
for (const ileti of ILETI) k.koy(ileti);
let alinan;
while ((alinan = k.al()) !== null) kuyrukTuketicisi.isle(alinan);

const g = gunluk(), akisTuketicisi = teslimatci();
for (const ileti of ILETI) g.ekle(ileti);
let konum = 0;
for (const ileti of g.oku(konum)) { akisTuketicisi.isle(ileti); konum += 1; }
const konumOnce = konum;

const sonradan = teslimatci();
for (const ileti of g.oku(0)) sonradan.isle(ileti);

console.log(`kuyruk: konan ${k.sayac().konan}, tuketildikten sonra bekleyen ${k.uzunluk()}`);
console.log(`  sonradan eklenen birimin kuyruktan kendi basina okudugu gecmis = ${k.uzunluk()} kayit`);
console.log(`  gecmis ancak tuketicinin kaydi disa acilarak alinir: ${kuyrukTuketicisi.kayit().length} kayit, baska tuketiciye kurulan bag = 1`);
console.log(`akis: gunluk uzunlugu ${g.uzunluk()}, ilk tuketicinin konumu ${konumOnce}`);
console.log(`  sonradan eklenen birimin konum 0'dan okudugu gecmis = ${sonradan.kayit().length} kayit, baska tuketiciye kurulan bag = 0`);
console.log(`  ilk tuketicinin konumu ${konumOnce} -> ${konum}, gunluk uzunlugu ${g.uzunluk()}`);
```

```sh
node gecmis.mjs
```

```
kuyruk: konan 20, tuketildikten sonra bekleyen 0
  sonradan eklenen birimin kuyruktan kendi basina okudugu gecmis = 0 kayit
  gecmis ancak tuketicinin kaydi disa acilarak alinir: 20 kayit, baska tuketiciye kurulan bag = 1
akis: gunluk uzunlugu 20, ilk tuketicinin konumu 20
  sonradan eklenen birimin konum 0'dan okudugu gecmis = 20 kayit, baska tuketiciye kurulan bag = 0
  ilk tuketicinin konumu 20 -> 20, gunluk uzunlugu 20
```

Kuyruk boşaldıktan sonra sonradan eklenen birimin kendi başına okuyabildiği geçmiş sıfır kayıttır.
Aynı bilgiyi almanın tek yolu var olan tüketicinin kaydını dışa açması, yani iki tüketici arasında
yeni bir bağ kurulmasıdır: yeni birim artık yayımcıya değil, bir başka tüketiciye bağlıdır.
Akışta aynı sayı yirmidir ve kurulan bağ sıfır; yeni birim yalnız günlüğü tanır. İkinci okuma ilk
tüketicinin konumunu değiştirmedi ve günlüğün uzunluğu artmadı — tüketici sayısı bilme
yükümlülüğünü büyütmüyor.

Seçim ölçütü budur: iş bir kez yapılıp bitecekse kuyruk yeterlidir; sonradan eklenen birimlerin
geçmişten kendi durumunu kurması gerekiyorsa akış gerekir ve bedeli kaydın saklanmasıdır.

## Özet

- Depo yayımcı ile tüketiciyi ayırır: yirmi iletiyi bırakan yayımcı eşzamanlı düzende 60 iş
  birimini kendi zamanından harcadı, kuyruk düzeninde 0 harcadı ve depoda 20 ileti bekledi.
- Toplam iş iki düzende de 60'tır ve iki düzen aynı kaydı üretti; kuyruk işi azaltmaz, zamanını
  değiştirir.
- Kuyruk hız farkını saklar: tur başına 5 üretim ile 3 tüketimde bekleyen ileti tepe değeri 11
  oldu ve üretim durduktan üç tur sonra kuyruk boşaldı.
- Sıra tek tüketicide korundu (tersine çift 0), ikinci tüketici eklendiğinde bitiş sırası üç
  yerde bozuldu; ikinci tüketici eklemek sıra garantisinden vazgeçmektir.
- Sekiz ileti dokuz kez teslim edildiğinde anahtarsız tüketici 9, anahtarlı tüketici 8 teslimat
  kaydı açtı; iş birimi ikisinde de 27'dir — depo sırayı garanti eder, tekliği etmez.
- Kuyrukta tüketmek silmektir: sonradan eklenen birimin okuyabildiği geçmiş 0 kayıt, akışta 20
  kayıttır ve akışta başka bir tüketiciye bağ kurulmaz.

## Sonraki Adım

Bu konu birimlerin birbiriyle nasıl konuştuğunu ele aldı. Altı biçemde aynı üç şey ölçüldü:
birimin karşı tarafı bilme yükümlülüğü, bağlanma yönü ve sınırdan geçen veri. İstemci ile sunucu,
eşler, model ile görünüm, boru ile süzgeç, yayımcı ile abone, kuyruğun iki ucu — hepsi bu üç
sayıyla karşılaştırıldı. Ancak bütün bu ölçümler bir soruyu sormadan geçti: bu birimler kaç ayrı
parça olarak yayınlanıyor? Bu dersteki kuyruk da, önceki dersteki veriyolu da tek bir çalışan
programın içindeydi; yayımla–abone ol düzeni için ikinci bir süreç gerekmedi, bir nesne yetti.
Etkileşim biçemi ile parça sayısı birbirinden bağımsız iki karardır. Sonraki konu ikincisini
sorar: yazılım kaç ayrı dağıtım biriminde yayınlanır ve bu sayı neyi değiştirir.
