İçeriğe geç
academia.sh

Ders 09 / 16

Yük Dengeleyici ve Ters Vekil

İki bileşenin aynı programla anlatılması ve ayrımın nerede olduğu: tek bir aktarma programının kural tablosuyla iki rolü de üstlenmesi, rollerin ayrı süreçlere bölünmesiyle tek süreçte toplanmasının durak zinciri ve durdurma etkisi üzerinden ölçülmesi, ayrımın sorumluluk ve konum olarak yazılması ve durak eklemenin giriş kursundaki bileşik erişilebilirlik hesabına faturasının çıkarılması.

İçindekiler

Önceki üç ders dengeleyiciyi bir yönlendirme aygıtı olarak kurdu: isteği alır, bir kural uygular, bir sürece iletir, yanıtı geri yazar. Aynı tanım Sunucu Tarafı Temelleri kursunda başka bir bileşen için de verilmişti. Ters vekil (reverse proxy) de isteği alır, bir kurala göre arka sunuculardan birine iletir ve yanıtı geri yazar. İki tanım birbirinden ayrılmıyor.

Bu ders örtüşmeyi kabul ederek başlar ve ayrımı başka bir yerde arar. İkisi de yeniden tanımlanmaz; ölçülen şey, iki rolün aynı süreçte toplanmasıyla ayrı süreçlere bölünmesinin ne değiştirdiğidir.

Aynı Program, İki Kural Tablosu

Örtüşmenin en kısa kanıtı tek bir programdır. Aşağıdaki aktarıcı bir kural tablosu alır: yol öneki, hedef listesi. Liste tek elemanlıysa yapılan iş yönlendirmedir — hangi bileşene. Listede aynı bileşenin birden çok kopyası varsa yapılan iş dağıtımdır — hangi kopyaya. İkisi aynı tabloda yan yana durabilir.

// vekil/aktarici.mjs — tek bir aktarma programi. Kural tablosu iki rolu de anlatir:
//   "/varlik=8891;*=8850"            -> yola gore ayri bilesenlere yonlendirme (ters vekil)
//   "*=8892,8893,8894"               -> ayni bilesenin kopyalari arasinda dagitim (yuk dengeleyici)
//   "/varlik=8891;*=8892,8893,8894"  -> ikisi birden (birlesik surec)
// Kullanim: node vekil/aktarici.mjs <nokta> <ad> <kural>
import { createServer, request } from "node:http";

const [nokta, ad, kuralMetni] = [Number(process.argv[2]), process.argv[3], process.argv[4] ?? ""];
const KURAL = kuralMetni.split(";").filter(Boolean)
  .map((p) => { const [onek, liste] = p.split("="); return { onek, hedef: liste.split(",").map(Number), sira: 0 }; });

if (Number.isInteger(nokta) === false || KURAL.length === 0) {
  console.log("kullanim: node vekil/aktarici.mjs <nokta> <ad> <onek=hedef[,hedef];...>");
} else createServer((istekDisi, yanitDisi) => {
  yanitDisi.sendDate = false;
  const yol = new URL(istekDisi.url, "http://yerel").pathname;
  const k = KURAL.find((r) => r.onek !== "*" && yol.startsWith(r.onek)) ?? KURAL.find((r) => r.onek === "*");
  if (k === undefined) { yanitDisi.writeHead(404).end("kural yok"); return; }
  const hedef = k.hedef[k.sira++ % k.hedef.length];
  const onceki = istekDisi.headers["x-durak"];
  const istekIc = request({ port: hedef, path: istekDisi.url, method: istekDisi.method,
    headers: { ...istekDisi.headers, "x-durak": onceki === undefined ? ad : `${onceki} > ${ad}` } },
  (yanitIc) => { yanitDisi.writeHead(yanitIc.statusCode, yanitIc.headers); yanitIc.pipe(yanitDisi); });
  istekIc.on("error", () => yanitDisi.writeHead(502, { "x-durak": `${ad} > ulasilamadi` }).end("yok"));
  istekDisi.pipe(istekIc);
}).listen(nokta, "127.0.0.1");

Zincirin sonundaki süreçler yalnız adlarını bildirir; x-durak başlığı istek boyunca birikerek isteğin dokunduğu durakları taşır.

// vekil/uc.mjs — zincirin sonundaki surec: adini x-durak basligina ekler
import { createServer } from "node:http";

const [port, ad] = [Number(process.argv[2]), process.argv[3]];

if (Number.isInteger(port) === false) console.log("kullanim: node vekil/uc.mjs <nokta> <ad>");
else createServer((istek, yanit) => {
  yanit.sendDate = false;
  const onceki = istek.headers["x-durak"];
  yanit.writeHead(200, { "content-type": "application/json",
    "x-durak": onceki === undefined ? ad : `${onceki} > ${ad}` });
  yanit.end(JSON.stringify({ durum: "aktarma merkezinde", bolge: "35" }));
}).listen(port, "127.0.0.1");
// vekil/istemci.mjs — iki yolu da dener, dokunulan durak zincirini ve yanitlanan istegi yazar
import { request } from "node:http";

const [nokta, etiket] = [Number(process.argv[2]), process.argv[3] ?? "-"];
const YOL = ["/varlik/stil.css", "/takip"];
const ADET = 6;

function bir(yol) {
  return new Promise((coz) => {
    const r = request({ port: nokta, path: yol }, (y) => {
      y.resume();
      y.on("end", () => coz({ kod: y.statusCode, durak: y.headers["x-durak"] ?? "-" }));
    });
    r.on("error", () => coz({ kod: 0, durak: "baglanti yok" }));
    r.end();
  });
}

if (Number.isInteger(nokta) === false) console.log("kullanim: node vekil/istemci.mjs <nokta> <etiket>");
else for (const yol of YOL) {
  const zincir = new Map();
  let yanitlanan = 0;
  for (let i = 0; i < ADET; i += 1) {
    const s = await bir(yol);
    if (s.kod === 200) yanitlanan += 1;
    zincir.set(s.durak, (zincir.get(s.durak) ?? 0) + 1);
  }
  const d = [...zincir].sort().map(([a, n]) => `${a} (${n})`).join("  |  ");
  console.log(`${etiket.padEnd(22)} ${yol.padEnd(17)} ${String(yanitlanan)}/${ADET}  ${d}`);
}

Düzenek aynı işi iki yerleşimle koşar. Ayrı yerleşimde iki aktarıcı süreci vardır: tv içerik yolu ile uygulama yolunu ayırır, yd kopyalar arasında dağıtır. Birleşik yerleşimde tek bir tvyd süreci ikisini de yapar. Her yerleşimde kopya listesini tutan süreç durdurulur ve hangi yolun ayakta kaldığı sayılır.

# olc.sh — ayni is iki yerlesimle: iki ayri aktarici sureci, sonra tek birlesik surec.
# Ikinci asamada kopya listesini tutan surec durduruluyor ve hangi yolun ayakta kaldigi sayiliyor.
node vekil/uc.mjs 8891 statik & S=$!
node vekil/uc.mjs 8892 k1 & K1=$!
node vekil/uc.mjs 8893 k2 & K2=$!
node vekil/uc.mjs 8894 k3 & K3=$!

node vekil/aktarici.mjs 8860 tv "/varlik=8891;*=8850" & TV=$!
node vekil/aktarici.mjs 8850 yd "*=8892,8893,8894" & YD=$!
sleep 1
node vekil/istemci.mjs 8860 "ayri: tv + yd"
kill $YD; sleep 0.5
node vekil/istemci.mjs 8860 "ayri: yd durduruldu"
kill $TV; sleep 0.3

node vekil/aktarici.mjs 8861 tvyd "/varlik=8891;*=8892,8893,8894" & B=$!
sleep 1
node vekil/istemci.mjs 8861 "birlesik: tvyd"
kill $B; sleep 0.5
node vekil/istemci.mjs 8861 "birlesik: durduruldu"

kill $S $K1 $K2 $K3
ayri: tv + yd          /varlik/stil.css  6/6  tv > statik (6)
ayri: tv + yd          /takip            6/6  tv > yd > k1 (2)  |  tv > yd > k2 (2)  |  tv > yd > k3 (2)
ayri: yd durduruldu    /varlik/stil.css  6/6  tv > statik (6)
ayri: yd durduruldu    /takip            0/6  tv > ulasilamadi (6)
birlesik: tvyd         /varlik/stil.css  6/6  tvyd > statik (6)
birlesik: tvyd         /takip            6/6  tvyd > k1 (2)  |  tvyd > k2 (2)  |  tvyd > k3 (2)
birlesik: durduruldu   /varlik/stil.css  0/6  baglanti yok (6)
birlesik: durduruldu   /takip            0/6  baglanti yok (6)

İki Sayı, İki Yön

Bu sayılar ölçüm sınıfındadır ve belirlenimlidir. İki yerleşim iki ayrı sütunda ters sıralanıyor.

Durak sayısı. Ayrı yerleşimde takip isteği üç durağa dokunuyor: tv > yd > k1. Birleşik yerleşimde iki durağa: tvyd > k1. İçerik yolu ikisinde de iki duraktır. Her durak bir ağ atlaması, bir ayrıştırma ve bir yeniden yazma demektir; ayrı yerleşim aynı isteği bir kez fazla elden geçiriyor.

Yalıtım. Kopya listesini tutan süreç durdurulduğunda ayrı yerleşimde içerik yolu 6/6 yanıtlanmaya devam ediyor, yalnız takip yolu 0/6’ya düşüyor — ve düşüş bile tv üzerinden bir 502 olarak dönüyor, yani istemci bir yanıt alıyor. Birleşik yerleşimde ikisi de 0/6 ve istemci hiçbir yanıt alamıyor, çünkü bağlanacağı bir bağlantı noktası kalmıyor. Kopya listesinin değişmesi bir yapılandırma değişikliğidir ve sık olur; birleşik süreçte o değişiklik içerik yolunu da durdurur.

İki sayı aynı kararın iki yüzüdür: bölmek bir durak ekler, birleştirmek bir arıza yüzeyi ekler.

Ayrım Nerede

Aynı program iki rolü de üstlenebildiğine göre ayrım programda değildir. İki yerdedir.

Sorumluluk. Ters vekil “hangi bileşen” sorusunu yanıtlar: /varlik ile başlayan yol içerik sürecine, geri kalanı uygulamaya. Kural tablosundaki önekler farklı işler yapan bileşenlere işaret eder ve hedef listeleri tek elemanlıdır. Yük dengeleyici “hangi kopya” sorusunu yanıtlar: hedef listesi aynı işi yapan süreçlerden oluşur ve içlerinden hangisinin seçildiği isteğin sonucunu değiştirmez. İlk soruda yanlış seçim yanlış davranıştır, ikincisinde yalnız farklı bir kopyadır.

Konum. Ters vekil uygulama sınırında durur: sistemin dış yüzünü tek bir yol uzayı olarak gösterir ve arkasındaki bileşen bölünmesini gizler. Yük dengeleyici kopya sınırında durur: bir bileşenin kaç kopyayla çalıştığını gizler. İki gizleme farklı ölçeklerde işler ve bu yüzden ikisi zincirde farklı yerlerde durabilir — ölçümdeki tv > yd sırası bunun ifadesidir, tersi anlamsızdır.

Bir sürecin ikisini birden üstlenmesi bu ayrımı ortadan kaldırmaz, yalnız aynı yapılandırma dosyasına yazar. Ayrımı görünür tutan şey kural tablosunun biçimidir: hangi satırın öneki bir bileşen ayrımı, hangisinin hedef listesi bir kopya kümesidir.

Hesaba Geri Dönüş

Bölmenin faturası durak sayısında görünüyordu; o sayının hizmet erişilebilirliğindeki karşılığı K01’de kurulmuştu. Erişilebilirliğin Sayısal İfadesi dersi takip yolunu dört bileşenli seri bir zincir olarak almış, bileşen başına yüzde 99,9 ile bileşik erişilebilirliği yüzde 99,6006 ve aylık kesintiyi 172,5 dakika bulmuştu. Aktarıcı süreçler o zincire eklenen yeni bileşenlerdir.

// vekil/zincir.mjs — durak eklemenin K01 bilesik erisilebilirlik hesabindaki karsiligi
const AY = 30 * 24 * 60;           // dakika
const TABAN = 4;                   // K01 Erisilebilirligin Sayisal Ifadesi: dort bilesenli takip yolu
const BILESEN = 0.999;             // K01 varsayim: bilesen basina hizmet erisilebilirligi

const seri = (a, n) => a ** n;
const kopyali = (a, k) => 1 - (1 - a) ** k;
const dakika = (a) => (1 - a) * AY;

console.log("yerlesim              durak  zincir bileseni  bilesik erisilebilirlik  aylik kesinti  tabana gore");
const taban = seri(BILESEN, TABAN);
for (const [ad, durak, kopya] of [
  ["durak yok (K01)", 0, 1], ["birlesik surec", 1, 1], ["ayri iki surec", 2, 1],
  ["ayri, her biri 2 kopya", 2, 2], ["birlesik, 2 kopya", 1, 2]]) {
  const a = taban * seri(kopyali(BILESEN, kopya), durak);
  console.log(`${ad.padEnd(22)}${String(durak).padStart(6)}${String(TABAN + durak).padStart(17)}  ` +
    `${(a * 100).toFixed(4).padStart(22)}%  ${dakika(a).toFixed(1).padStart(11)} dk  ` +
    `${(dakika(a) - dakika(taban)).toFixed(1).padStart(9)} dk`);
}

const birlesik = taban * seri(BILESEN, 1), ayri = taban * seri(BILESEN, 2);
console.log(`\nrolleri iki surece bolmenin faturasi = ${(dakika(ayri) - dakika(birlesik)).toFixed(1)} dk/ay`);
console.log(`ayni fatura duraklar ikiser kopyali oldugunda = ` +
  `${(dakika(taban * seri(kopyali(BILESEN, 2), 2)) - dakika(taban * seri(kopyali(BILESEN, 2), 1))).toFixed(2)} dk/ay`);
console.log(`K01 aylik kesinti butcesi (%99.9) = ${((1 - 0.999) * AY).toFixed(1)} dk; ` +
  `ayri yerlesimin kesintisi butcenin ${(dakika(ayri) / ((1 - 0.999) * AY)).toFixed(2)} kati`);
yerlesim              durak  zincir bileseni  bilesik erisilebilirlik  aylik kesinti  tabana gore
durak yok (K01)            0                4                 99.6006%        172.5 dk        0.0 dk
birlesik surec             1                5                 99.5010%        215.6 dk       43.0 dk
ayri iki surec             2                6                 99.4015%        258.6 dk       86.0 dk
ayri, her biri 2 kopya     2                6                 99.6004%        172.6 dk        0.1 dk
birlesik, 2 kopya          1                5                 99.6005%        172.6 dk        0.0 dk

rolleri iki surece bolmenin faturasi = 43.0 dk/ay
ayni fatura duraklar ikiser kopyali oldugunda = 0.04 dk/ay
K01 aylik kesinti butcesi (%99.9) = 43.2 dk; ayri yerlesimin kesintisi butcenin 5.99 kati

Bu sayılar hesap sınıfındadır. İlk üç satır bölmenin faturasını veriyor: tek kopyalı duraklarla, rolleri iki sürece ayırmak aylık kesintiyi 215,6 dakikadan 258,6 dakikaya çıkarıyor — ayda 43,0 dakika. Sayı K01’in yüzde 99,9 hedefi için ayırdığı bütün aylık bütçeye (43,2 dakika) neredeyse eşittir. Yani rolleri ayırma kararı, tek başına, bir aylık kesinti bütçesi kadar tutuyor.

Dördüncü ve beşinci satır faturanın nereden geldiğini gösteriyor. Duraklar ikişer kopyalı olduğunda aynı bölme 0,04 dakikaya iniyor ve iki yerleşim ayırt edilemez hâle geliyor. K01’in kopya tablosundaki kural burada da geçerli: koşut kopyalarda kesinti payı üs alır. Durak eklemenin faturası durak eklemekten değil, durağı tek kopyalı bırakmaktan geliyor.

Son satır tabloyu bir ölçütle bitiriyor. Ayrı yerleşimin aylık kesintisi 258,6 dakikadır ve yüzde 99,9 bütçesinin 5,99 katıdır. Bu, yerleşimin kötü olduğunu değil, yüzde 99,9 hedefinin bileşen başına yüzde 99,9 ile karşılanamayacağını söyler; K01 aynı sonucu dört bileşenle bulmuştu, iki durak eklemek onu keskinleştiriyor. Bir kenar katmanı tasarlanırken durak sayısı bir mimari tercih değil, doğrudan bir erişilebilirlik kalemidir.

Özet

  • Yük dengeleyici ile ters vekil aynı programla yazılabilir: kural tablosunda hedef listesi tek elemanlıysa yapılan iş yönlendirme, aynı bileşenin kopyalarından oluşuyorsa dağıtımdır.
  • Ayrı yerleşimde takip isteği üç durağa dokundu (tv > yd > k1), birleşik yerleşimde ikiye (tvyd > k1); bölmek isteği bir kez fazla elden geçiriyor.
  • Kopya listesini tutan süreç durdurulduğunda ayrı yerleşimde içerik yolu 6/6 yanıtlanmaya devam etti, birleşik yerleşimde iki yol da 0/6 oldu ve istemci hiç yanıt alamadı.
  • Ayrım sorumluluktadır ve konumdadır: ters vekil “hangi bileşen” sorusunu uygulama sınırında, yük dengeleyici “hangi kopya” sorusunu kopya sınırında yanıtlar; ilkinde yanlış seçim yanlış davranış, ikincisinde yalnız farklı bir kopyadır.
  • Tek kopyalı duraklarla rolleri ayırmak aylık kesintiyi 215,6’dan 258,6 dakikaya çıkarıyor: ayda 43,0 dakika, yüzde 99,9 hedefinin bütün aylık bütçesi kadar.
  • Duraklar ikişer kopyalı olduğunda aynı fatura 0,04 dakikaya iniyor; durak eklemenin bedeli durağı tek kopyalı bırakmaktan gelir.

Sonraki Adım

Buraya kadarki beş ders bir şeyi hiç sorgulamadı: dengeleyicinin seçimi serbesttir. Sıralı kural, en az bağlantı kuralı ve iki yerleşimin hepsi, bir isteğin herhangi bir kopyaya gönderilebileceğini varsaydı; tutarlı karma bile seçimi kararlı tutmayı isterken zorunlu kılmıyordu. Kopyaların birbirinin eşi olmaktan çıktığı bir durum vardır: bir istemcinin önceki isteğinden kalan bir durum belirli bir kopyanın belleğinde durursa, o istemcinin sonraki isteği artık herhangi bir kopyaya gidemez. Seçim bir tercih olmaktan çıkıp bir kısıt hâline gelir. Sonraki ders bu kısıtı adlandırır ve ölçer: dağılımın ne kadar bozulduğunu, bir kopya düştüğünde ne kadar durumun kaybolduğunu ve kopya eklemenin tepe yükü neden düşürmediğini sayar.

İ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