İçeriğe geç
academia.sh

Ders 20 / 25

Yük Boşaltma

Aşırı yükte denetimli reddetme: eşzamanlılık eşiğine dayalı kabul denetimi, kabul edilenle reddedilenin gecikme farkının ölçümü, reddin HTTP'deki biçimi ve yeniden deneme fırtınasının önlenmesi.

İçindekiler

Bir önceki ders geri basıncı kurdu ve bir sınırla bitti: geri basınç sistemin iç halkalarında çalışır, dış ucunda duran istemciye taşınamaz. İstek işleyen bir sunucu, arka plan kuyruğu dolduğu için isteği süresiz bekletemez.

O zaman aşırı yükte ne yapılır. Yanıt, kabul edilmesi güç olduğu için sık atlanır: isteği reddetmek. Reddetmek bir arıza değil, bir tasarım kararıdır. Reddetmeyen bir sunucu istekleri kabul etmeye devam eder, her birinin gecikmesi büyür ve bir noktadan sonra hiçbir istek zamanında karşılanmaz. Bu duruma çöküş denir ve reddetmekten çok daha kötüdür: reddedilen istek en azından hızlıca reddedilmiştir. Bu ders reddin nasıl denetimli yapılacağını kurar ve iki durumun gecikmesini ölçerek karşılaştırır.

Kabul Denetimi

Yük boşaltma (load shedding), sistemin kapasitesini aşan işi kabul etmeyip geri çevirmesidir. Kararı veren yapıya kabul denetimi (admission control) denir ve iş başlamadan önce çalışır.

Kabul denetimi, hız sınırlamadan iki noktada ayrılır. Hız sınırlama belirli bir istemcinin belirli bir sürede yapabileceği istek sayısını sınırlar; ölçütü istemcidir ve amacı adil paylaşımdır. Kabul denetiminin ölçütü ise sistemin o anki durumudur; amacı, kabul edilmiş işlerin zamanında bitmesini korumaktır. Aynı istemci, sistem boşken kabul edilir, doluyken reddedilir.

Eşik Neye Konur

Eşiğin saniyedeki istek sayısına konması yaygın ama yanıltıcıdır. Saniyedeki istek sayısı işin süresini içermez: aynı hız, iş yirmi milisaniye sürüyorsa rahat, üç yüz milisaniye sürüyorsa taşırıcıdır.

Doğru ölçüt eşzamanlılıktır: aynı anda işlenen iş sayısı. Önceki derste kullanılan L=λWL = \lambda W bağıntısı bunu doğrudan verir; sistemdeki iş sayısı LL sabitlenirse, ortalama süre WW de sabitlenmiş olur. Eşzamanlılığı sabitlemek gecikmeyi sabitlemektir.

Aşağıdaki sunucu iki eşik taşır: aynı anda işlenebilecek iş sayısı ve kuyrukta bekleyebilecek iş sayısı. İkisinin toplamı, sistemin kabul ettiği en büyük yüktür.

// kabul.mjs — is sunucusu; iki kipte calisir
//   node kabul.mjs --sinirsiz    gelen her istek kuyruga alinir
//   node kabul.mjs --denetimli   esik asilinca istek 503 ile geri cevrilir
import { createServer } from "node:http";

const DENETIMLI = process.argv.includes("--denetimli");
const EN_COK_ESZAMANLI = 2;        // ayni anda islenen is sayisi
const EN_COK_KUYRUK = 2;           // kuyrukta bekleyebilecek is sayisi
const IS_SURESI = 300;             // bir isin suresi (ms)

let calisan = 0;
const kuyruk = [];

const isYap = () => new Promise((c) => setTimeout(c, IS_SURESI));

async function baslat(gorev) {
  calisan++;
  await isYap();
  calisan--;
  gorev.yanit.writeHead(200, { "content-type": "text/plain" });
  gorev.yanit.end(`${gorev.durum}\n`);
  const sonraki = kuyruk.shift();
  if (sonraki) baslat(sonraki);
}

createServer((istek, yanit) => {
  yanit.sendDate = false;
  if (calisan < EN_COK_ESZAMANLI) return baslat({ yanit, durum: "kabul" });
  if (!DENETIMLI || kuyruk.length < EN_COK_KUYRUK)
    return kuyruk.push({ yanit, durum: "kuyruk" });
  // Esik asildi: istek beklemeye alinmaz, ne zaman tekrar denenecegi bildirilir.
  yanit.writeHead(503, { "content-type": "text/plain", "retry-after": "1" });
  yanit.end("bosaltildi\n");
}).listen(8352, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8352"));

Ölçüm

Aşağıdaki betik sunucuyu iki kipte de ayağa kaldırır, her seferinde on isteği aynı anda gönderir ve sonuçları durum koduna ve yüz milisaniyelik gecikme dilimlerine göre gruplar. Bağlantı noktası 8352 keyfîdir ve boş olmalıdır.

#!/usr/bin/env bash
# kabul.mjs'yi iki kipte baslatir, her kipte on istegi ayni anda gonderir, sonucu ozetler.
A=http://127.0.0.1:8352

olc() {
  node kabul.mjs "$1" > /dev/null & sunucu=$!
  sleep 1
  pid=()
  for i in $(seq 1 10); do
    curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' "$A/is" & pid+=($!)
  done > ham.txt 2>&1
  wait "${pid[@]}"
  echo "--- $2 ---"
  awk '{ printf "durum=%s  gecikme=%2d dilim\n", $1, int($2 * 10) }' ham.txt | sort | uniq -c
  kill "$sunucu"
  sleep 0.5
}

olc --sinirsiz  "kabul denetimi yok: her istek kuyruga alinir"
olc --denetimli "kabul denetimi var: esik asilinca 503"
--- kabul denetimi yok: her istek kuyruga alinir ---
   2 durum=200  gecikme= 3 dilim
   2 durum=200  gecikme= 6 dilim
   2 durum=200  gecikme= 9 dilim
   2 durum=200  gecikme=12 dilim
   2 durum=200  gecikme=15 dilim
--- kabul denetimi var: esik asilinca 503 ---
   2 durum=200  gecikme= 3 dilim
   2 durum=200  gecikme= 6 dilim
   6 durum=503  gecikme= 0 dilim

Dilim sayısı yüz milisaniyelik birimdir; iş süresi üç yüz milisaniye olduğu için ilk çift üç dilimde, ikinci çift altı dilimde yanıt alır. Yavaş bir makinede dilim sayıları bir üste kayabilir.

İki kip aynı işi yapıyor, ama gecikme dağılımları taban tabana zıt.

Denetimsiz kipte hiçbir istek reddedilmedi ve kimse memnun olmadı: son iki istek bir buçuk saniye bekledi. Yük on yerine yüz istek olsaydı son isteğin gecikmesi on beş saniye olurdu. Kabul edilen her istek, kendinden önce kabul edilmiş herkesin gecikmesini taşıyor.

Denetimli kipte dört istek karşılandı, altısı sıfırıncı dilimde, yani bekletilmeden reddedildi. Kabul edilenlerin gecikmesi üç ve altı dilimde kaldı; sistem, kabul ettiği isteklere verdiği sözü tuttu.

Fark tek cümleyle şudur: kabul denetimi, belirsiz bir gecikmeyi kesin bir redde çevirir. İstemci için bu iyileşmedir, çünkü redde karşı bir davranışı vardır — yeniden denemek, işi ertelemek, kullanıcıya bilgi vermek. Belirsiz bir beklemeye karşı hiçbir davranışı yoktur.

Reddin Biçimi

Reddin nasıl bildirildiği, reddedilme kararı kadar önemlidir. Aşağıdaki betik sunucuyu doygunluğa getirip bir isteğin ham yanıtını gösterir.

#!/usr/bin/env bash
# Doygunluga ulasmis sunucudan gelen reddin yanitini basliklariyla gosterir.
node kabul.mjs --denetimli > /dev/null & sunucu=$!
sleep 1
A=http://127.0.0.1:8352

pid=()
for i in 1 2 3 4; do curl -sS -o /dev/null "$A/is" & pid+=($!); done   # 2 calisan + 2 kuyruk
sleep 0.1
echo "--- doygunlukta gelen istegin yaniti ---"
curl -sS -D - "$A/is"
wait "${pid[@]}"
kill "$sunucu"
--- doygunlukta gelen istegin yaniti ---
HTTP/1.1 503 Service Unavailable
content-type: text/plain
retry-after: 1
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

bosaltildi

Üç ayrıntı bilinçli seçimdir. Durum kodu 503’tür, 500 değil: 500 sunucunun hatalı olduğunu, 503 ise geçici olarak hizmet veremediğini söyler. İkisi arasındaki fark istemcinin davranışını belirler — 500 alan istemci yeniden denememelidir, 503 alan denemelidir. Retry-After başlığı bu denemenin ne zaman yapılacağını saniye cinsinden bildirir; istemcinin tahmin yürütmesi gerekmez. Ve gövde kısadır: doygun bir sunucunun uzun bir hata gövdesi üretmesi, kaçınmaya çalıştığı işin kendisidir.

Bağlantı başlıkları çalışma zamanının varsayılan ayarına bağlıdır.

Neyi Boşaltmalı

Buraya kadar reddedilecek istek, eşiği aşan istekti. Bu en yalın politikadır ve bir kusuru vardır: tüm işleri eşit görür. Oysa kütüphane servisinde bir ödünç iade işlemi ile aylık rapor talebi eşit değildir.

İki ölçüt bu ayrımı verir. Önem sırası ile boşaltmada her işe bir sınıf verilir ve doygunlukta önce düşük sınıf reddedilir; kritik sınıfa ayrılmış bir eşzamanlılık payı her zaman boş tutulur. Son tarihe göre boşaltmada ise her iş bir gecikme bütçesiyle gelir; kuyrukta beklerken bütçesi dolan iş, sırası geldiğinde işlenmeden atılır. İkincisi özellikle değerlidir, çünkü kuyruğun başındaki işin çoktan anlamsızlaştığı durumu yakalar — istemci çekilmiş olabilir.

Her iki politikada da boşaltma kararı sabit bir eşikten değil, ölçülen bir göstergeden beslenmelidir: kuyrukta geçirilen süre, işçilerin doluluğu, dış bağımlılığın gecikmesi. Sabit eşik, yükün biçimi değiştiğinde ya erken ya geç boşaltır.

Yeniden Deneme Fırtınası

Boşaltmanın kendisi yeni bir tehlike üretir. Aynı anda reddedilen altı istemci, Retry-After süresi dolduğunda aynı anda geri döner. Bu, sunucuyu doygun tutan bir döngüye yol açar ve ona yeniden deneme fırtınası (retry storm) denir.

Panzehiri bu kursta zaten kuruldu: mesajlaşma konusunda tanıtılan üstel geri çekilme ve sarsıntı. Retry-After değeri bir taban süredir; istemci bu tabana rastgele bir sarsıntı ekler ve her başarısız denemede süreyi büyütür. Ayrıca istemci tarafında bir üst deneme sınırı bulunmalıdır, yoksa reddedilen iş sonsuza dek geri gelir.

Sunucu tarafında da bir önlem vardır: reddedilen isteğin kütüğe yazılması, kabul edilenden daha ucuz olmalıdır. Doygunlukta her reddi ayrıntılı kütüklemek, boşaltmanın kazandırdığı kapasiteyi kütükleme işine harcar.

Özet

  • Yük boşaltma, kapasiteyi aşan işi kabul etmeyip geri çevirmektir; kararı veren kabul denetimi iş başlamadan önce çalışır.
  • Eşik saniyedeki istek sayısına değil eşzamanlılığa konur; L=λWL = \lambda W gereği sistemdeki iş sayısını sabitlemek gecikmeyi sabitler.
  • Ölçüm, denetimsiz sunucuda gecikmenin 3 dilimden 15 dilime doğru yayıldığını, denetimli sunucuda ise kabul edilenlerin 3–6 dilimde kaldığını ve fazlanın sıfırıncı dilimde reddedildiğini gösterdi.
  • Kabul denetimi belirsiz bir gecikmeyi kesin bir redde çevirir; 503 ile Retry-After istemciye ne yapacağını söyler, 500 ise yeniden denenmemesi gereken bir hatayı bildirir.
  • Boşaltma yeniden deneme fırtınası doğurur; panzehiri istemci tarafında üstel geri çekilme ile sarsıntı, sunucu tarafında ise reddin ucuz tutulmasıdır.

Sonraki Adım

Buraya kadar kurulan her şey işin sunucu tarafındaki dayanıklılığıyla ilgiliydi: iş bir işçiye düşer, zamanında tetiklenir, ilerlemesi kaydedilir, kuyruk taşmaz, aşırı yükte denetimli olarak reddedilir. Bu zincirin bir ucu hâlâ açık. İşi başlatan istemci, işin kabul edildiğini öğrendi ama sonucun ne zaman hazır olduğunu bilmiyor. Uzun süren raporun ilerleme yüzdesi, rezervasyon sırasındaki konumun değişmesi, şube stok sayacının güncellenmesi — bunların hepsi sunucu tarafında hazır, istemci tarafında görünmez. Sonraki konu bu bilginin istemciye nasıl ulaştığını ele alır ve en yalın yoldan, yani istemcinin tekrar tekrar sormasından başlar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat