---
title: 'Sıfır Kesintili Yayın'
source: 'https://academia.sh/tr/kurslar/backend-uretim/sifir-kesintili-yayin'
course: 'Sunucu Güvenliği ve Üretime Alma'
language: tr
updated: '2026-08-17T18:06:50+00:00'
license: 'CC BY-SA 4.0'
---

# Sıfır Kesintili Yayın

Yayın sırasında düşen istek üç düzende sayılır: doğrudan değiştirme, mavi–yeşil ve aşamalı yayın. Düşen istek eşdeğer kesinti saniyesine çevrilir, bağlantı boşaltmanın uçuştaki isteklere etkisi ölçülür ve geri almanın adım sayısı karşılaştırılır.

Önceki dersin son ölçümü düğüm sayısını ikiden üçe çıkardı ve bunun oturumlara ne yaptığını
saydı. Düğüm kümesini değiştiren ikinci olay yayındır: bir sürüm durur, başkası açılır ve
bir süre ikisi birlikte hizmet verir. Ölçekleme bu değişikliği seyrek yapar, yayın sık.

Bu ders bölünmüş ödünç sisteminin ödünç servisini üç ayrı düzende yayınlar ve tek bir şeyi
sayar: **yayın sırasında kaç istek düştü**. Düşen istek sayısı, akan yükün hızına
bölündüğünde kesinti saniyesine çevrilir; kesinti "sistem kapalıydı" demenin sayılabilir
biçimidir.

- **DP13.** Yeni sürecin açılıp istek almaya hazır olması, düzenekte **20 isteklik** bir
  pencereye denk gelir. Gerçek bir kurulumda bu pencere sürecin açılış süresine ve o anki
  istek hızına bağlıdır; burada sabit tutulur ki düzenler karşılaştırılabilsin.
- **DP14.** Ödünç servisinin iki sürümü aynı sözleşmeyi karşılar; istemci hangi sürüme
  düştüğünü ayırt etmez. Sözleşmenin kendisi değişiyorsa bu ders geçerli değildir.
- **DP15.** Yük sıralı akar: her istek bir öncekinin yanıtından sonra gönderilir. Eşdeğer
  kesinti saniyesi, saniyede 50 istek varsayımıyla hesaplanır.

## Ölçüm Düzeneği

İki süreç türü var. Birincisi ödünç servisinin bir sürümü; hangi sürüm olduğunu ve
kapanırken ne yapacağını dışarıdan alır.

```js
// surum.mjs — odunc servisinin bir surumu; SURUM, GECIKME ve kapanma turu disaridan gelir
import { createServer } from 'node:http';

const SURUM = process.env.SURUM;
const GECIKME = Number(process.env.GECIKME ?? 0);
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));

const sunucu = createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, 'http://yerel');
  if (yol.pathname === '/kapan') {
    yanit.writeHead(200).end('kapaniyor');
    if (yol.searchParams.get('tur') === 'zarif') {
      // Yeni baglanti alinmaz, ucustaki istekler bitirilir, sonra surec cikar.
      sunucu.close(() => process.exit(0));
      sunucu.closeIdleConnections();
      setTimeout(() => process.exit(0), 5000).unref();
    } else {
      process.exit(0);                      // ucustaki istekler yarida kalir
    }
    return;
  }
  if (GECIKME) await bekle(GECIKME);
  yanit.writeHead(200, { 'content-type': 'application/json' })
    .end(JSON.stringify({ surum: SURUM, port: Number(process.env.PORT) }));
});
sunucu.listen(Number(process.env.PORT), () => console.log(`hazir ${SURUM}`));
```

İkincisi ön yüzdür: istemcinin bildiği tek adres. Arkasındaki havuzu bir denetim ucundan
değiştirir; havuz boşken istek gidecek yer bulamaz ve 503 döner. Yayın düzenleri arasındaki
bütün fark, bu havuzun hangi sırayla değiştirildiğidir.

```js
// onyuz.mjs — istegi havuzdaki sureclere sirayla dagitir; havuz bosken 503 doner
import { createServer } from 'node:http';

let havuz = [];
let sira = 0;

createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, 'http://yerel');
  if (yol.pathname === '/havuz') {
    havuz = (yol.searchParams.get('hedef') ?? '').split(',').filter(Boolean);
    sira = 0;
    return yanit.writeHead(200).end(havuz.join(','));
  }
  if (havuz.length === 0) return yanit.writeHead(503).end('havuz bos');
  const hedef = havuz[sira++ % havuz.length];
  try {
    const ust = await fetch(`http://127.0.0.1:${hedef}${istek.url}`);
    yanit.writeHead(ust.status, { 'content-type': 'application/json' }).end(await ust.text());
  } catch {
    yanit.writeHead(502).end('ust akis yanit vermedi');
  }
}).listen(Number(process.env.PORT), () => console.log('onyuz hazir'));
```

```sh
SURUM=v1 PORT=8731 node surum.mjs > /dev/null &
ESKI=$!
PORT=8730 node onyuz.mjs > /dev/null &
ON=$!
sleep 0.6
curl -s -o /dev/null -w 'havuz bosken durum kodu: %{http_code}\n' 'http://127.0.0.1:8730/odunc'
curl -s -o /dev/null 'http://127.0.0.1:8730/havuz?hedef=8731'
curl -s -w '\n' 'http://127.0.0.1:8730/odunc'
kill $ESKI $ON
```

```
havuz bosken durum kodu: 503
{"surum":"v1","port":8731}
```

Düşen isteğin tanımı bu 503'tür: istemci istedi, sistem veremedi.

## Üç Düzen

Üç düzen de aynı işi yapar — `v1` gider, `v2` gelir — ve aynı yük altında koşar. Ayrıldıkları
yer, eski sürümün ne zaman durdurulduğudur. Doğrudan değiştirmede eskiyi durdurmak yeniyi
açmanın önkoşuludur; mavi–yeşilde yeni sürüm eski hâlâ hizmet verirken ayağa kalkar;
aşamalıda havuz karışık kalır ve yeni sürümün payı adım adım büyür.

```js
// yayin.mjs — uc yayin duzeninde akan yuk: dogrudan degistirme, mavi-yesil, asamali
import { spawn } from 'node:child_process';

const ONYUZ = 8730, HIZ = 50, YUK = 20, PENCERE = 20;
const surecler = new Map();

async function baslat(surum, port) {
  const cocuk = spawn(process.execPath, ['surum.mjs'], { stdio: ['ignore', 'pipe', 'inherit'],
    env: { ...process.env, SURUM: surum, PORT: String(port) } });
  await new Promise((c) => cocuk.stdout.once('data', c));
  surecler.set(port, cocuk);
}
const durdur = (portlar) => portlar.forEach((p) => { surecler.get(p)?.kill('SIGKILL'); surecler.delete(p); });
const havuza = (portlar) => fetch(`http://127.0.0.1:${ONYUZ}/havuz?hedef=${portlar}`).then((y) => y.text());

async function akit(n) {
  const say = { v1: 0, v2: 0, dusen: 0 };
  for (let i = 0; i < n; i += 1) {
    try {
      const y = await fetch(`http://127.0.0.1:${ONYUZ}/odunc`);
      if (y.ok) say[(await y.json()).surum] += 1;
      else { await y.text(); say.dusen += 1; }
    } catch { say.dusen += 1; }
  }
  return say;
}

async function dogrudan() {
  const adim = [], asama = [];
  await baslat('v1', 8731); await baslat('v1', 8732); await havuza('8731,8732');
  asama.push(['eski surum hizmet veriyor', await akit(YUK)]);
  adim.push('eski surecleri durdur ve havuzu bosalt'); durdur([8731, 8732]); await havuza('');
  asama.push(['eski durdu, yeni acilmadi', await akit(PENCERE)]);
  adim.push('yeni surumu ayaga kaldir'); await baslat('v2', 8733); await baslat('v2', 8734);
  adim.push('havuza yeni surumu koy'); await havuza('8733,8734');
  asama.push(['yeni surum hizmet veriyor', await akit(YUK)]);
  const geri = ['yeni surecleri durdur ve havuzu bosalt', 'eski surumu ayaga kaldir', 'havuza eski surumu koy'];
  durdur([8733, 8734]); await havuza('');
  const geriSay = await akit(PENCERE);
  await baslat('v1', 8731); await baslat('v1', 8732); await havuza('8731,8732');
  durdur([8731, 8732]);
  return { ad: 'dogrudan degistirme', adim, asama, geri, geriSay };
}

async function maviYesil() {
  const adim = [], asama = [];
  await baslat('v1', 8731); await baslat('v1', 8732); await havuza('8731,8732');
  asama.push(['mavi hizmet veriyor', await akit(YUK)]);
  adim.push('yesili ayaga kaldir'); await baslat('v2', 8733); await baslat('v2', 8734);
  asama.push(['yesil ayakta, havuzda degil', await akit(YUK)]);
  adim.push('havuzu yesile cevir'); await havuza('8733,8734');
  asama.push(['havuz yesile cevrildi', await akit(YUK)]);
  const geriSay = await (async () => { await havuza('8731,8732'); return akit(YUK); })();
  adim.push('maviyi bosalt ve kapat');
  durdur([8731, 8732, 8733, 8734]);
  return { ad: 'mavi-yesil', adim, asama, geri: ['havuzu maviye geri cevir'], geriSay };
}

async function asamali() {
  const adim = [], asama = [];
  await baslat('v1', 8731); await baslat('v1', 8732); await havuza('8731,8732');
  asama.push(['yeni surumun payi yok', await akit(YUK)]);
  adim.push('yeni surumun ilk dugumunu ayaga kaldir'); await baslat('v2', 8733);
  adim.push('havuzda uc dugumun biri yeni'); await havuza('8731,8732,8733');
  asama.push(['uc dugumun biri yeni', await akit(YUK)]);
  adim.push('ikinci yeni dugum, bir eski dugum havuzdan cikar'); await baslat('v2', 8734);
  await havuza('8731,8733,8734');
  asama.push(['uc dugumun ikisi yeni', await akit(YUK)]);
  adim.push('havuz butunuyle yeni surume gecer'); await havuza('8733,8734');
  asama.push(['butun havuz yeni', await akit(YUK)]);
  const geriSay = await (async () => { await havuza('8731,8732'); return akit(YUK); })();
  adim.push('eski dugumleri bosalt ve kapat');
  durdur([8731, 8732, 8733, 8734]);
  return { ad: 'asamali', adim, asama, geri: ['havuzu eski surume geri cevir'], geriSay };
}

const onyuz = spawn(process.execPath, ['onyuz.mjs'], { stdio: ['ignore', 'pipe', 'inherit'],
  env: { ...process.env, PORT: String(ONYUZ) } });
await new Promise((c) => onyuz.stdout.once('data', c));

const sonuc = [await dogrudan(), await maviYesil(), await asamali()];
for (const s of sonuc) {
  console.log(`${s.ad} — yayin adimi ${s.adim.length}`);
  for (const [etiket, say] of s.asama) {
    console.log(`  ${etiket.padEnd(32)} v1=${String(say.v1).padEnd(3)} v2=${String(say.v2).padEnd(3)} dusen=${say.dusen}`);
  }
  console.log(`  ${`geri alma, ${s.geri.length} adim`.padEnd(32)} ${' '.repeat(14)} dusen=${s.geriSay.dusen}`);
}
console.log('');
console.log(`akan yuk saniyede ${HIZ} istek varsayimiyla`);
console.log('duzen                yayin adimi  dusen istek  esdeger kesinti (sn)  geri alma adimi  geri almada dusen');
for (const s of sonuc) {
  const dusen = s.asama.reduce((t, [, y]) => t + y.dusen, 0);
  console.log(`${s.ad.padEnd(20)} ${String(s.adim.length).padEnd(12)} ${String(dusen).padEnd(12)} ` +
    `${(dusen / HIZ).toFixed(2).padEnd(21)} ${String(s.geri.length).padEnd(16)} ${s.geriSay.dusen}`);
}
onyuz.kill('SIGKILL');
```

```
dogrudan degistirme — yayin adimi 3
  eski surum hizmet veriyor        v1=20  v2=0   dusen=0
  eski durdu, yeni acilmadi        v1=0   v2=0   dusen=20
  yeni surum hizmet veriyor        v1=0   v2=20  dusen=0
  geri alma, 3 adim                               dusen=20
mavi-yesil — yayin adimi 3
  mavi hizmet veriyor              v1=20  v2=0   dusen=0
  yesil ayakta, havuzda degil      v1=20  v2=0   dusen=0
  havuz yesile cevrildi            v1=0   v2=20  dusen=0
  geri alma, 1 adim                               dusen=0
asamali — yayin adimi 5
  yeni surumun payi yok            v1=20  v2=0   dusen=0
  uc dugumun biri yeni             v1=14  v2=6   dusen=0
  uc dugumun ikisi yeni            v1=7   v2=13  dusen=0
  butun havuz yeni                 v1=0   v2=20  dusen=0
  geri alma, 1 adim                               dusen=0

akan yuk saniyede 50 istek varsayimiyla
duzen                yayin adimi  dusen istek  esdeger kesinti (sn)  geri alma adimi  geri almada dusen
dogrudan degistirme  3            20           0.40                  3                20
mavi-yesil           3            0            0.00                  1                0
asamali              5            0            0.00                  1                0
```

Doğrudan değiştirmenin kesintisi bir yanlışlıktan doğmuyor; **düzenin kendisinden**
doğuyor. Eskiyi durdurmadan yeniyi aynı yere koyamayan bir kurulumda, aradaki pencere
boyunca gidecek yer yoktur. Yirmi istek düştü ve saniyede elli istekle bu 0,40 saniyelik
kesintiye denk geliyor. Sayı küçük görünür; günde on yayın yapan bir ekipte dört saniyelik
bir kesintidir ve tamamı kullanıcıya hata olarak gider.

Mavi–yeşil aynı işi sıfır düşen istekle bitirdi. Farkı ikinci satırda görünüyor: yeşil
ayağa kalkarken mavi hizmet vermeye devam etti, yani açılış penceresi yükün dışında kaldı.
Bunun bedeli, o pencere boyunca **iki sürümün birden ayakta** olmasıdır — yayın anında
kaynak gereksinimi iki katına çıkar.

Aşamalı yayının tablosu ikinci ve üçüncü satırda karışık pay gösteriyor: üç düğümün biri
yeniyken yirmi isteğin altısı, ikisi yeniyken on üçü yeni sürüme gitti. Ön yüz havuzu her
değiştiğinde sırayı sıfırladığı için bu paylar koşumdan bağımsızdır. Aşamalı yayının
kazandırdığı şey kesinti değil — mavi–yeşilde de sıfırdı — **hatalı sürümün kaç isteğe
dokunacağıdır**: yeni sürüm ikinci aşamada bozuk çıksaydı yirmi isteğin altısı etkilenirdi,
mavi–yeşilde yirmisi de.

Geri alma sütunu bu dersin asıl kararını taşıyor. Mavi–yeşilde ve aşamalıda geri alma tek
adımdır — havuzu eski sürüme çevirmek — ve sıfır istek düşürür, çünkü eski süreçler hâlâ
ayaktadır. Doğrudan değiştirmede geri alma yeni bir yayındır: üç adım, yirmi düşen istek.
Buradan okunacak kural, eski süreçlerin ayakta tutulduğu sürenin adı olan **geri alma
penceresidir**; o pencere kapandığı anda geri alma tek adım olmaktan çıkar.

## Bağlantı Boşaltma

Havuz değiştiğinde eski düğüme yeni istek gitmez, ama o anda **uçuşta** olan istekler
vardır. Onların ne olduğu, eski sürecin nasıl kapatıldığına bağlıdır.

```js
// bosaltma.mjs — eski dugum kapatilirken ucusta kalan istekler: sert ve zarif kapanma
import { spawn } from 'node:child_process';

const PORT = 8735, UCUSTA = 8, GECIKME = 500;
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));

async function olc(tur) {
  const cocuk = spawn(process.execPath, ['surum.mjs'], { stdio: ['ignore', 'pipe', 'inherit'],
    env: { ...process.env, SURUM: 'v1', PORT: String(PORT), GECIKME: String(GECIKME) } });
  await new Promise((c) => cocuk.stdout.once('data', c));

  const ucus = Array.from({ length: UCUSTA }, () =>
    fetch(`http://127.0.0.1:${PORT}/odunc`).then((y) => y.ok).catch(() => false));
  await bekle(150);                                  // sekiz istek ucusta, hicbiri bitmedi
  await fetch(`http://127.0.0.1:${PORT}/kapan?tur=${tur}`).catch(() => {});
  const sonuc = await Promise.all(ucus);
  cocuk.kill('SIGKILL');
  return { tur, tamamlanan: sonuc.filter(Boolean).length };
}

console.log(`kapanma aninda ucusta ${UCUSTA} istek var, her biri ${GECIKME} ms suruyor`);
console.log('kapanma  tamamlanan  dusen');
for (const tur of ['sert', 'zarif']) {
  const s = await olc(tur);
  console.log(`${s.tur.padEnd(8)} ${String(s.tamamlanan).padEnd(11)} ${UCUSTA - s.tamamlanan}`);
}
```

```
kapanma aninda ucusta 8 istek var, her biri 500 ms suruyor
kapanma  tamamlanan  dusen
sert     0           8
zarif    8           0
```

Doğru ayarın ölçüsü budur: zarif kapanmada uçuştaki sekiz isteğin sekizi de tamamlandı,
sert kapanmada sekizi de düştü. Aradaki tek fark, kapanma işaretini alan sürecin hemen
çıkması ile açık isteklerin bitmesini beklemesidir.

## Sessiz Olan Ne

Bu iki ölçüm aynı anın iki tarafını gösteriyor ve birbirini yalanlıyor gibi görünüyor.
Yayın tablosunda mavi–yeşilin düşen istek sayısı sıfırdı; boşaltma tablosunda aynı geçişte
sekiz istek düştü. İkisi de doğru. Yayın tablosunu üreten yük, ön yüzden geçen **yeni**
isteklerdir ve havuz çevrildikten sonra hepsi yeşile gitti, hiçbiri düşmedi. Sekiz düşen
istek, havuz çevrilmeden **önce** maviye yönlendirilmiş ve hâlâ yanıt bekleyen isteklerdir.
Ön yüzün sayaçlarında görünmezler.

Yanlış ayarın sessiz sonucu tam olarak budur: kapanma türü sert bırakıldığında yayın
"sıfır kesintili" raporlanmaya devam eder, çünkü ön yüz hiç boş havuz görmemiştir ve
düğümler açılışta hazır bildirmiştir. Uygulama hata da vermez — süreç düzgün bir çıkış
koduyla kapanır. Fark yalnız uçuştaki isteklerin sayısında görünür ve o sayı yükle birlikte
büyür: saniyede elli istek alan, ortalama yarım saniye süren bir uçta her kapanmada
yaklaşık yirmi beş istek uçuştadır.

Ayarların durduğu yer de düzenler arasında değişiyor. Havuz üyeliği **tek bir yerde**, ön
yüzdedir; bir yayın sırasında birkaç kez değişir ama hep aynı yerden. Kapanma türü ise
**her düğümde** durur: sürece hangi işaretin gönderileceği süreç yöneticisinin ayarıdır,
o işaretin nasıl karşılandığı uygulama kodundadır. İkisinin uyuşmaması — süreç yöneticisi
zarif kapanma işareti gönderirken uygulamanın onu dinlememesi — tabloda sert kapanma
satırını üretir, hiçbir hata kaydı bırakmadan.

## Özet

- Düşen istek, ön yüzün gidecek yer bulamadığı istektir; düşen istek sayısı yük hızına
  bölününce eşdeğer kesinti saniyesini verir.
- Doğrudan değiştirmede kesinti düzenin kendisinden gelir: eskiyi durdurmak yeniyi açmanın
  önkoşulu olduğu için açılış penceresi boyunca yirmi istek düştü, 0,40 saniye.
- Mavi–yeşil ve aşamalı yayın sıfır istek düşürdü; bedeli, yayın penceresinde iki sürümün
  birden ayakta durmasıdır.
- Aşamalı yayının kazancı kesinti değil etki alanıdır: üç düğümün biri yeniyken yirmi
  isteğin altısı yeni sürüme gitti.
- Geri alma mavi–yeşilde ve aşamalıda tek adım ve sıfır düşen istek; doğrudan değiştirmede
  üç adım ve yirmi düşen istektir. Eski süreçlerin ayakta kaldığı süre geri alma
  penceresidir.
- Sert kapanmada uçuştaki sekiz isteğin sekizi de düştü, zarif kapanmada sıfırı; bu fark
  ön yüzün sayaçlarında görünmez.

## Sonraki Adım

Bu dersin üç düzeni de bir varsayıma yaslandı: iki sürüm aynı anda ayakta durabilir ve
aynı isteği aynı biçimde karşılayabilir. Süreçler için doğru olan bu varsayım, altlarındaki
şema için kendiliğinden doğru değildir. Mavi–yeşil geçişte iki sürüm aynı veritabanını
okur; aşamalı yayında bu birlikte yaşama daha da uzun sürer. Sonraki ders bu pencereyi
ölçer: göç ile kod yayınının hangi sırada gittiği, yanlış sırada kaç isteğin hata aldığı,
iki sürümün aynı şemayla çalışabildiği aralık ve geri alınamayan adımın nerede durduğu.
