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

# Mesaj Kuyrukları

Kuyruğun en küçük hâlinin kurulması: yazma, alma, onay ve görünmezlik süresi, onaylanmayan iletinin yeniden teslim edilmesinin ölçülmesi, rekabet eden tüketicilerin tek iletiyi tek tarafa vermesi ve kuyruk derinliğinin üretim ile tüketim hızına bağlı davranışı.

Önceki ders yan işleri ödünç kaydıyla aynı işlemde giden kutusuna yazdı ve dokuz işin
süreç yeniden başladıktan sonra hâlâ orada olduğunu gösterdi. Orada bıraktığımız sorular
şunlardı: bu satırları kim okuyacak, iki okuyucu aynı satırı aynı anda alırsa ne olacak,
bir okuyucu satırı alıp işlemeden çökerse iş nasıl geri dönecek?

Üçünün de yanıtı tek bir yapıdadır. **Mesaj kuyruğu** (message queue), bir tarafın yazdığı
ve başka bir tarafın okuduğu, okunanın ne zaman silineceğine okuyanın karar verdiği kalıcı
bir listedir. Bu ders kuyruğu en küçük hâliyle kurar ve her iletinin kaç kez teslim
edildiğini sayar.

## Kuyruğun Dört İşlemi

Yazan tarafa **üretici** (producer), okuyan tarafa **tüketici** (consumer) denir. Aralarındaki
sözleşme dört işlemden oluşur. Üretici `yaz` ile iletiyi kuyruğa bırakır. Tüketici `al` ile
bir ileti ister. İş bitince `onayla` ile iletinin silinmesini söyler. Dördüncüsü işlem değil,
bir süredir: alınan ileti onaylanana kadar başka tüketiciye verilmez, ama bu koruma sonsuza
kadar sürmez — **görünmezlik süresi** (visibility timeout) dolduğunda ileti yeniden görünür
olur.

Aşağıdaki model bu dördünü tutar. Zaman gerçek saatten değil, açıkça ilerletilen bir sanal
saatten okunur; böylece ölçümler makineden bağımsız olur.

```js
// kuyruk.mjs — onay ve gorunmezlik suresi olan en kucuk kuyruk modeli
export class Kuyruk {
  constructor(gorunmezlik = 30) {
    this.gorunmezlik = gorunmezlik;   // alinan ileti kac ms boyunca baskasina verilmez
    this.iletiler = [];               // {id, govde, gorunur, teslim}
    this.sonrakiId = 1;
    this.simdi = 0;                   // sanal saat: gercek zamana bagli degil
    this.toplamTeslim = 0;
  }
  yaz(govde) {                        // uretici tarafi
    this.iletiler.push({ id: this.sonrakiId++, govde, gorunur: 0, teslim: 0 });
    return this.iletiler.at(-1).id;
  }
  al() {                              // tuketici tarafi
    const i = this.iletiler.find((m) => m.gorunur <= this.simdi);
    if (i === undefined) return null;
    i.gorunur = this.simdi + this.gorunmezlik;
    i.teslim += 1;
    this.toplamTeslim += 1;
    return { id: i.id, govde: i.govde, teslim: i.teslim };
  }
  onayla(id) {                        // is bitti: ileti kuyruktan silinir
    const once = this.iletiler.length;
    this.iletiler = this.iletiler.filter((m) => m.id !== id);
    return once !== this.iletiler.length;
  }
  ilerlet(ms) { this.simdi += ms; }
  get derinlik() { return this.iletiler.length; }
  get gorunur() { return this.iletiler.filter((m) => m.gorunur <= this.simdi).length; }
}
```

Beklenen durumda üretici beş iş yazar, tek tüketici hepsini alıp onaylar.

```js
// mutlu-yol.mjs — bes is yazilir, tek tuketici hepsini alip onaylar
import { Kuyruk } from "./kuyruk.mjs";
const k = new Kuyruk();
const ISLER = [
  "gecikme_bildirimi odunc=1", "gecikme_bildirimi odunc=2", "stok_ozeti sube=1",
  "rezervasyon_ilerlet kitap=3", "rapor_satiri odunc=1",
];
for (const is of ISLER) k.yaz(is);
console.log(`uretildi=${ISLER.length}  kuyruk derinligi=${k.derinlik}`);

let islenen = 0;
for (let ileti = k.al(); ileti !== null; ileti = k.al()) {
  islenen += 1;
  k.onayla(ileti.id);
}
console.log(`islenen=${islenen}  toplam teslim=${k.toplamTeslim}  kalan derinlik=${k.derinlik}`);
```

```sh
node mutlu-yol.mjs
```

```
uretildi=5  kuyruk derinligi=5
islenen=5  toplam teslim=5  kalan derinlik=0
```

Beş iş, beş teslim, sıfır kalan. Üretici ile tüketici arasında hiçbir doğrudan çağrı yok:
üretici tüketicinin çalışıp çalışmadığını bilmiyor, tüketici işi kimin ürettiğini bilmiyor.
Kuyruk ikisini **zamanda** ayırır — bu, alan olaylarındaki gevşek bağlanmanın üstüne eklenen
ikinci bir ayrımdır.

## Onay Neden Ayrı Bir Adım

`al` iletiyi silmez. Silseydi, işi alan tüketicinin çökmesi işin kaybolması demek olurdu.
Bunun yerine ileti geçici olarak görünmez yapılır ve silme kararı onaya bırakılır. Aşağıdaki
ölçüm bir tüketicinin ikinci işi onaylamadan sonlandığı durumu koşturur.

```js
// onaysiz.mjs — onaylanmayan ileti gorunmezlik suresi dolunca yeniden teslim edilir
import { Kuyruk } from "./kuyruk.mjs";
const k = new Kuyruk(30);
for (const is of ["gecikme_bildirimi odunc=1", "stok_ozeti sube=1", "rapor_satiri odunc=1"])
  k.yaz(is);

// Birinci tuketici: ikinci isi alirken sureci sonlaniyor, onay gondermiyor.
const cokecek = new Set(["stok_ozeti sube=1"]);
for (let ileti = k.al(); ileti !== null; ileti = k.al()) {
  const durum = cokecek.has(ileti.govde) ? "onaysiz kaldi" : "onaylandi";
  if (durum === "onaylandi") k.onayla(ileti.id);
  console.log(`t=${k.simdi} tuketici-1 ileti=${ileti.id} teslim=${ileti.teslim} ${durum}`);
}
console.log(`t=${k.simdi} derinlik=${k.derinlik} gorunur=${k.gorunur}`);

k.ilerlet(30);                       // gorunmezlik suresi doldu
console.log(`t=${k.simdi} derinlik=${k.derinlik} gorunur=${k.gorunur}`);

// Ikinci tuketici ayni isi bulur.
for (let ileti = k.al(); ileti !== null; ileti = k.al()) {
  k.onayla(ileti.id);
  console.log(`t=${k.simdi} tuketici-2 ileti=${ileti.id} teslim=${ileti.teslim} onaylandi`);
}
console.log(`toplam teslim=${k.toplamTeslim}  yazilan ileti=3  kalan derinlik=${k.derinlik}`);
```

```sh
node onaysiz.mjs
```

```
t=0 tuketici-1 ileti=1 teslim=1 onaylandi
t=0 tuketici-1 ileti=2 teslim=1 onaysiz kaldi
t=0 tuketici-1 ileti=3 teslim=1 onaylandi
t=0 derinlik=1 gorunur=0
t=30 derinlik=1 gorunur=1
t=30 tuketici-2 ileti=2 teslim=2 onaylandi
toplam teslim=4  yazilan ileti=3  kalan derinlik=0
```

Üç ileti yazıldı, dört teslim yapıldı. İkinci ileti iki kez teslim edildi çünkü ilk teslim
onaysız kaldı. Ölçümün en önemli satırı ortadaki ikisidir: onaysız ileti kuyrukta duruyordu
(`derinlik=1`) ama görünür değildi (`gorunur=0`); süre dolunca görünür oldu. Görünmezlik
süresi, "bu iş şu an başka biri tarafından yapılıyor" varsayımının son kullanma tarihidir.

Sürenin seçimi bir ödünleşmedir. İşin en uzun süresinden kısa seçilirse, iş hâlâ sürerken
ileti ikinci bir tüketiciye verilir ve aynı iş iki kez yapılır. Çok uzun seçilirse, gerçekten
çöken bir tüketicinin bıraktığı iş uzun süre kimseye ulaşmaz. Uzun süren işler için doğru
çözüm süreyi büyütmek değil, tüketicinin çalışırken süreyi uzatmasıdır.

Buradan kuyruğun temel garantisi çıkar: **onaylanmayan iş kaybolmaz, ama kaybolmaması
yinelenebilmesi pahasınadır.** Teslim sayısı iletinin işlenme sayısı değildir; yalnız
kuyruğun onu kaç kez verdiğidir.

## Rakip Tüketiciler

Kuyruğun ikinci temel özelliği, aynı kuyruktan okuyan birden çok tüketicinin işi
**paylaşmasıdır**. Bu düzene rekabet eden tüketiciler denir: her ileti tam olarak bir tüketiciye
gider, çünkü alınan ileti diğerleri için görünmez olur.

```js
// rakip-tuketiciler.mjs — ayni kuyruktan okuyan uc tuketici; her ileti tek tuketiciye gider
import { Kuyruk } from "./kuyruk.mjs";
const k = new Kuyruk();
for (let i = 1; i <= 12; i++) k.yaz(`gecikme_bildirimi odunc=${i}`);

const alan = new Map();                       // ileti -> onu isleyen tuketici
const sayac = { "tuketici-1": 0, "tuketici-2": 0, "tuketici-3": 0 };
const adlar = Object.keys(sayac);
for (let sira = 0; k.derinlik > 0; sira++) {
  const ad = adlar[sira % adlar.length];
  const ileti = k.al();
  if (ileti === null) break;
  alan.set(ileti.id, ad);
  sayac[ad] += 1;
  k.onayla(ileti.id);
}
console.log(`dagitim: ${Object.entries(sayac).map(([a, n]) => `${a}=${n}`).join("  ")}`);
console.log(`farkli ileti sayisi=${alan.size}  toplam teslim=${k.toplamTeslim}  kalan=${k.derinlik}`);
```

```sh
node rakip-tuketiciler.mjs
```

```
dagitim: tuketici-1=4  tuketici-2=4  tuketici-3=4
farkli ileti sayisi=12  toplam teslim=12  kalan=0
```

On iki iş üç tüketiciye dörder dağıldı. Farklı ileti sayısı ile toplam teslim sayısının eşit
olması, hiçbir iletinin iki tüketiciye birden gitmediğini söyler. Gecikme bildirimi işi
üçe bölünebildiği için tüketici eklemek doğrudan kapasite eklemektir; ödünç verme isteğinin
kendisinde böyle bir bölünebilirlik yoktur.

## Kuyruk Derinliği

Kuyrukta bekleyen ileti sayısına **kuyruk derinliği** denir ve tek bir şeyi ölçer: üretim
hızı ile tüketim hızının farkını. Aşağıdaki koşum her turda beş iş üretir, her tüketici
turda üç iş bitirir.

```js
// derinlik.mjs — uretim hizi tuketim hizini asinca kuyruk derinligi buyur
import { Kuyruk } from "./kuyruk.mjs";

function kosum(tuketiciSayisi, tur = 6, uretim = 5, tuketimHizi = 3) {
  const k = new Kuyruk();
  const derinlikler = [];
  for (let t = 1; t <= tur; t++) {
    for (let i = 0; i < uretim; i++) k.yaz(`is t=${t} n=${i}`);
    for (let i = 0; i < tuketiciSayisi * tuketimHizi; i++) {
      const ileti = k.al();
      if (ileti === null) break;
      k.onayla(ileti.id);
    }
    k.ilerlet(1000);
    derinlikler.push(k.derinlik);
  }
  return derinlikler;
}

for (const n of [1, 2, 3]) {
  const d = kosum(n);
  console.log(`tuketici=${n}  tur sonu derinlikleri: ${d.join(" ")}`);
}
```

```sh
node derinlik.mjs
```

```
tuketici=1  tur sonu derinlikleri: 2 4 6 8 10 12
tuketici=2  tur sonu derinlikleri: 0 0 0 0 0 0
tuketici=3  tur sonu derinlikleri: 0 0 0 0 0 0
```

Tek tüketiciyle derinlik her turda ikişer artıyor; kuyruk işi yutmuyor, yalnız erteliyor.
İki tüketici kapasiteyi altıya çıkardığı için derinlik sıfırda kalıyor, üçüncü tüketici hiçbir
şey eklemiyor. Kuyruk bir hız düzenleyici değildir; ani yükü düzleştirir, sürekli farkı
kapatmaz. Derinliğin zamanla artması tüketici eksikliğinin doğrudan göstergesidir ve
izlenmesi gereken ilk ölçüttür.

## Kuyruğun Verdiği ve Vermediği

Kuyruk üç şeyi verir: iletinin kalıcılığı, işin bölünebilmesi ve tarafların zamanda
ayrılması. Vermedikleri de aynı ölçüde belirleyicidir.

Tek teslim vermez. Ölçümde gördüğümüz gibi onaylanmayan ileti yeniden teslim edilir; işlemenin
tekrar altında etkisiz olması tüketicinin sorumluluğudur. Etkisiz İşlemler dersinde kurulan
`islenen_ileti` bekçisi tam bu koşul içindir.

Sıra vermez. Rakip tüketiciler işi paylaştığı anda, iki iletinin bitiş sırası yazılma
sırasından farklı olabilir. Aynı kitabın iki olayı iki tüketiciye düşerse ikincisi önce
bitebilir.

Yayın vermez. Bir ileti tam olarak bir tüketiciye gider. Aynı olayın hem bildirim hem rapor
hem de arama dizini tarafından görülmesi gerekiyorsa kuyruk tek başına yetmez.

## Özet

- Kuyruk dört işlemle tanımlanır: üreticinin yazması, tüketicinin alması, işi bitirince
  onaylaması ve alınan iletinin görünmezlik süresi boyunca başkasına verilmemesi.
- Onaysız kalan ileti kuyrukta durur ama görünmez olur; süre dolunca yeniden görünür oldu ve
  üç yazılan ileti için toplam dört teslim ölçüldü.
- Görünmezlik süresi işin en uzun süresinden kısa seçilirse aynı iş iki kez yapılır, çok uzun
  seçilirse çöken tüketicinin işi uzun süre bekler.
- Rakip tüketicilerde her ileti tek tarafa gider: on iki iş üç tüketiciye dörder dağıldı,
  farklı ileti sayısı ile toplam teslim sayısı eşit çıktı.
- Kuyruk derinliği üretim ile tüketim hızının farkını ölçer; tek tüketicide her turda ikişer
  arttı, ikinci tüketici eklendiğinde sıfırda kaldı, üçüncüsü hiçbir şey değiştirmedi.

## Sonraki Adım

Kuyruğun vermediklerinden sonuncusu bir sonraki dersin konusudur. Ödünç verme olayı yalnız
bildirim servisini değil, şube stok özetini ve aylık raporu da ilgilendiriyor. Aynı olayı üç
tarafa da ulaştırmak için üç ayrı kuyruğa üç kopya yazmak, üreticiyi yeniden tüketicilerin
listesine bağlar — tam da kaçınmak istediğimiz şeye. Sonraki ders bu bağı koparan yayımla–abone
ol modelini kurar, tek iletinin abone sayısına göre kaç teslime dönüştüğünü sayar ve yavaş
bir abonenin diğerlerini etkileyip etkilemediğini ölçer.
