İçeriğe geç
academia.sh

Ders 07 / 16

Katman 7 Dengeleme

Kararın her istek için yeniden verilmesi ve isteğin içeriğine bakılması: aynı istemci karışımında istek sapmasının dört kattan bire inmesi, okuma ile yazma akışlarının ayrı kopya havuzlarına bölünmesi, dış bağlantıların iç bağlantılara indirgenmesi ve bölmenin giriş kursu hesabında kaç kopyaya mal olduğunun ölçülmesi.

İçindekiler

Önceki dersin ölçtüğü 4,00 kat sapma ve ayrılamayan iki akış aynı nedenden geliyordu: karar, karar için gereken bilgi ortaya çıkmadan önce veriliyordu. Bilgi bir katman yukarıda vardır. Yol, yöntem, başlıklar ve sorgu dizgisi bağlantı kurulduktan hemen sonra akmaya başlar; bir dengeleyici bunları okumayı seçerse her istek için yeniden karar verebilir.

Bu düzeyin adı katman 7 dengelemedir. Katman 7 uygulama katmanıdır ve HTTP orada tanımlıdır; protokolün kendisi Ağ Modelleri ve Protokoller kursunun konusudur ve burada yeniden anlatılmaz. Bu dersin işi kararın istek başına ve içeriğe bakılarak verilmesinin ne kazandırdığını ve ne ödettiğini ölçmektir.

Kararın Her İstek İçin Verilmesi

Katman 7 dengeleyici bir bağlantıyı iki parçaya böler: istemciyle kurduğu bağlantı ve kopyayla kurduğu bağlantı. Arada isteği ayrıştırır, yolu okur, bir havuz seçer ve isteği yeniden yazarak gönderir.

// k7/dengeleyici.mjs — katman 7 dengeleyici: her istek icin yol okunur, havuz secilir, sirali
// dagitim yapilir. Kullanim: node k7/dengeleyici.mjs <nokta> <sayac.json> <okuma> <yazma>
import { createServer, request, Agent } from "node:http";
import { writeFileSync } from "node:fs";

const nokta = Number(process.argv[2]);
const sayacYolu = process.argv[3];
const HAVUZ = {
  okuma: (process.argv[4] ?? "").split(",").filter(Boolean).map(Number),
  yazma: (process.argv[5] ?? "").split(",").filter(Boolean).map(Number),
};
const sira = { okuma: 0, yazma: 0 };
const ajan = new Map();                       // kopya basina tek kalici ic baglanti
const gorulen = new WeakSet();                // ic baglantilarin tekil sayimi
const sayac = { karar: 0, disBaglanti: 0, icBaglanti: 0, dagilim: {} };

function say(kopya, havuz) {
  const a = (sayac.dagilim[kopya] ??= { okuma: 0, yazma: 0 });
  a[havuz] += 1;
}

if (Number.isInteger(nokta) === false || HAVUZ.okuma.length === 0) {
  console.log("kullanim: node k7/dengeleyici.mjs <nokta> <sayac.json> <okuma> <yazma>");
} else {
  const s = createServer((istekDisi, yanitDisi) => {
    const yol = new URL(istekDisi.url, "http://yerel").pathname;   // her istekte okunur
    const havuz = yol === "/olay" ? "yazma" : "okuma";
    const liste = HAVUZ[havuz];
    const hedef = liste[sira[havuz]++ % liste.length];
    sayac.karar += 1;
    say(hedef, havuz);
    if (ajan.has(hedef) === false) ajan.set(hedef, new Agent({ keepAlive: true, maxSockets: 1 }));
    const istekIc = request({ port: hedef, path: istekDisi.url, method: istekDisi.method,
      headers: { ...istekDisi.headers, forwarded: `for="${istekDisi.socket.remoteAddress}"` },
      agent: ajan.get(hedef) }, (yanitIc) => {
      yanitDisi.writeHead(yanitIc.statusCode, yanitIc.headers);
      yanitIc.pipe(yanitDisi);
    });
    istekIc.on("socket", (sok) => {
      if (gorulen.has(sok) === false) { gorulen.add(sok); sayac.icBaglanti += 1; }
    });
    istekIc.on("error", () => yanitDisi.writeHead(502).end("kopyaya ulasilamadi"));
    istekDisi.pipe(istekIc);
  });
  s.on("connection", () => { sayac.disBaglanti += 1; });
  s.listen(nokta, "127.0.0.1");
  process.on("SIGTERM", () => { writeFileSync(sayacYolu, JSON.stringify(sayac)); process.exit(0); });
}

Kopyalar bu düzenekte yalnız kendi adını bildirir; sayım artık dengeleyicidedir, çünkü kararı o veriyor ve hangi isteği nereye gönderdiğini biliyor.

// k7/kopya.mjs — uygulama kopyasi: yalnizca kendi adini bildirir, sayim dengeleyicide tutulur
import { createServer } from "node:http";

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

if (Number.isInteger(port) === false) console.log("kullanim: node k7/kopya.mjs <nokta> <ad>");
else createServer((istek, yanit) => {
  yanit.sendDate = false;
  yanit.writeHead(200, { "content-type": "application/json", "x-kopya": ad });
  yanit.end(JSON.stringify({ durum: "aktarma merkezinde", bolge: "35", adim: 4 }));
}).listen(port, "127.0.0.1");

İstemci önceki dersin karışımını aynen tekrarlar — altı istemci, eşitsiz paylar, istemci başına tek kalıcı bağlantı — ve bir şey ekler: yol karışımı. Beş istekten biri durum olayıdır, böylece düzenekteki okuma/yazma oranı K01’in hesapladığı orana yakın durur.

// k7/istemci.mjs — 02. dersin ayni karisimi: her istemci tek kalici baglanti, esitsiz paylar.
// Yol karisimi K01'in okuma/yazma oranini yaklasik tutar: bes istekten biri durum olayidir.
import { Agent, request } from "node:http";

const nokta = Number(process.argv[2]);
const PAY = (process.argv[3] ?? "1").split(",").map(Number);

function bir(agent, no, yol) {
  return new Promise((coz, sap) => {
    const r = request({ port: nokta, path: `${yol}?no=${no}`, agent }, (y) => {
      y.resume();
      y.on("end", () => coz(y.headers["x-kopya"] ?? "-"));
    });
    r.on("error", sap);
    r.end();
  });
}

if (Number.isInteger(nokta) === false) console.log("kullanim: node k7/istemci.mjs <nokta> <pay,pay,...>");
else {
  let g = 0, takip = 0, olay = 0;
  for (let i = 0; i < PAY.length; i += 1) {
    const agent = new Agent({ keepAlive: true, maxSockets: 1 });
    for (let j = 0; j < PAY[i]; j += 1) {
      const yol = g % 5 === 4 ? "/olay" : "/takip";
      if (yol === "/olay") olay += 1; else takip += 1;
      await bir(agent, `I${i}-${j}`, yol);
      g += 1;
    }
    agent.destroy();
  }
  console.log(`${PAY.length} istemci, ${g} istek: takip ${takip}, olay ${olay} ` +
    `(oran ${(takip / olay).toFixed(2)}; K01'de tepe okuma/yazma = 4.29)`);
}
// k7/rapor.mjs — dengeleyicinin sayac dosyasini tabloya cevirir
import { existsSync, readFileSync } from "node:fs";

const yol = process.argv[2];
if (yol === undefined || existsSync(yol) === false) {
  console.log("kullanim: node k7/rapor.mjs <sayac.json>");
  process.exit(0);
}
const d = JSON.parse(readFileSync(yol, "utf8"));
const satir = Object.entries(d.dagilim).sort();
const toplam = satir.map(([, v]) => v.okuma + v.yazma);

console.log("kopya  takip  olay  toplam");
for (const [k, v] of satir) {
  console.log(`${k.padEnd(5)}  ${String(v.okuma).padStart(5)}  ${String(v.yazma).padStart(4)}  ` +
    `${String(v.okuma + v.yazma).padStart(6)}`);
}
console.log(`karar = ${d.karar} (istek basina 1), dis baglanti = ${d.disBaglanti}, ` +
  `ic baglanti = ${d.icBaglanti}`);
console.log(`istek sapmasi = ${(Math.max(...toplam) / Math.min(...toplam)).toFixed(2)}`);

Düzenek aynı dört kopyayı iki kez ayağa kaldırır. Birinci koşumda dördü de tek havuzdadır; ikincisinde üçü okuma, biri yazma havuzundadır. Bağlantı noktaları ortama bağlıdır.

# olc.sh — ayni istemci karisimi iki yerlesimle: dort kopya tek havuzda, sonra okuma/yazma ayri
for yerlesim in "8881,8882,8883,8884:8881,8882,8883,8884" "8881,8882,8883:8884"; do
  OKUMA=${yerlesim%%:*}; YAZMA=${yerlesim##*:}
  node k7/kopya.mjs 8881 k1 & K1=$!
  node k7/kopya.mjs 8882 k2 & K2=$!
  node k7/kopya.mjs 8883 k3 & K3=$!
  node k7/kopya.mjs 8884 k4 & K4=$!
  node k7/dengeleyici.mjs 8845 sayac.json "$OKUMA" "$YAZMA" & D=$!
  sleep 1
  echo "okuma havuzu = $OKUMA   yazma havuzu = $YAZMA"
  node k7/istemci.mjs 8845 "1,2,4,8,16,32"
  kill -TERM $D; sleep 0.3
  node k7/rapor.mjs sayac.json
  kill $K1 $K2 $K3 $K4; sleep 0.3
  echo
done
okuma havuzu = 8881,8882,8883,8884   yazma havuzu = 8881,8882,8883,8884
6 istemci, 63 istek: takip 51, olay 12 (oran 4.25; K01'de tepe okuma/yazma = 4.29)
kopya  takip  olay  toplam
8881      13     3      16
8882      13     3      16
8883      13     3      16
8884      12     3      15
karar = 63 (istek basina 1), dis baglanti = 6, ic baglanti = 4
istek sapmasi = 1.07

okuma havuzu = 8881,8882,8883   yazma havuzu = 8884
6 istemci, 63 istek: takip 51, olay 12 (oran 4.25; K01'de tepe okuma/yazma = 4.29)
kopya  takip  olay  toplam
8881      17     0      17
8882      17     0      17
8883      17     0      17
8884       0    12      12
karar = 63 (istek basina 1), dis baglanti = 6, ic baglanti = 4
istek sapmasi = 1.42

Sapmanın Kaybolması

Bu sayılar ölçüm sınıfındadır ve belirlenimlidir. Karşılaştırma önceki dersin tablosuyla yapılır: aynı altı istemci, aynı eşitsiz paylar, aynı toplam istek sayısı.

Katman 4’te istek sapması 4,00’dı. Katman 7’de 1,07. Sapmanın kaybolmasının nedeni tabloda görünüyor: karar sayısı 63, yani istek başına 1. Otuz iki isteği tek bir bağlantıdan gönderen istemci artık otuz iki ayrı karara konu oluyor ve o istekler dört kopyaya dağılıyor. Kalan 1,07 tam bölünememekten geliyor — 63 istek dörde bölündüğünde 16, 16, 16 ve 15 olur.

İkinci koşum ayrı bir kazanç gösteriyor. Aynı dengeleyici yolu okuduğu için /takip isteklerini okuma havuzuna, /olay isteklerini yazma havuzuna gönderiyor: okuma kopyaları 17’şer takip isteği, yazma kopyası 12 olay isteği aldı ve çapraz sızma yok. Katman 4’te olanaksız olan ayrım burada bir liste ayrımına indi. Toplam sapmanın 1,42’ye çıkması bir kusur değil: havuzlar artık farklı akışlara hizmet ettiği için tek bir sapma sayısıyla karşılaştırılmaları anlamsızdır, her havuz kendi akış hızına göre boyutlanır.

Bağlantı sütunu üçüncü bir farkı taşıyor. Dış bağlantı 6, iç bağlantı 4. Dengeleyici istemcilerin altı bağlantısını kopya başına tek kalıcı bağlantıya indiriyor; katman 4’te böyle bir indirgeme olanaksızdı, çünkü orada her dış bağlantının bir iç bağlantı karşılığı vardı. Kopyaların gördüğü bağlantı sayısı istemci sayısından bağımsız hâle geliyor.

Ödenen Bedel

Kazançların hepsi aynı satırda fiyatlanıyor: 63 karar. Katman 4 aynı yükü 6 kararla geçirdi. Karar başına yapılan iş de artmıştır — dengeleyici istek satırını ayrıştırır, başlıkları kopyalar, kendi forwarded başlığını ekler ve isteği yeniden yazar. Bu işin süresi ölçülmüyor, çünkü ayrıştırma maliyeti gerçekleştirime ve makineye bağlıdır; ölçülen şey işin kaç kez yapıldığıdır.

İkinci bedel sorumluluk devridir. Dengeleyici isteği ayrıştırdığı andan itibaren istekle ilgili kararların bir kısmının sahibi olur: hangi başlıkların iletileceği, gövdenin tamponlanıp tamponlanmayacağı, bir kopya yanıt vermezken isteğin başka bir kopyaya yeniden gönderilip gönderilmeyeceği. Katman 4 dengeleyicide bu soruların hiçbiri yoktu, çünkü orada bir istek kavramı yoktu. İçeriğe bakan bir dengeleyici, uygulamanın bir parçası hâline gelir.

Üçüncü bedel bağımlılıktır. Katman 7 kuralları protokole ve yol adlarına bağlıdır. /olay yolu /durum-olayi olarak yeniden adlandırıldığında dengeleyicinin yapılandırması da değişmelidir; katman 4 dengeleyici bu değişiklikten hiç etkilenmezdi.

Hesaba Geri Dönüş

Önceki iki ders tek bir havuz varsayıp 513,89 istek/s tepe hızı böldü. İçerik farkındalıklı yönlendirme bu bölmeyi ikiye ayırma seçeneğini açıyor ve K01’in iki akış hızı ayrı ayrı hesaplanmıştı: tepe okuma 416,67 istek/s, tepe yazma 97,22 istek/s.

// k7/havuz.mjs — K01 akis hizlarinin tek havuz ile ayri havuzlara bolunmesi ve yazma tasmasinin etkisi
const OKUMA = 416.67;              // K01 Kabaca Buyukluk Hesabi: tepe okuma istek/s
const YAZMA = 97.22;               // K01: tepe yazma istek/s
const DOYUM = 400;                 // 01. ders varsayim Y1
const GUVENLI = 200;               // 01. ders: Y1 x Y2

const kopya = (hiz) => Math.ceil(hiz / GUVENLI);
const doluluk = (hiz, n) => hiz / (n * DOYUM);

const tek = kopya(OKUMA + YAZMA);
const ok = kopya(OKUMA), ya = kopya(YAZMA);
console.log(`tek havuz : ${tek} kopya, doluluk ${doluluk(OKUMA + YAZMA, tek).toFixed(3)}`);
console.log(`ayri havuz: okuma ${ok} + yazma ${ya} = ${ok + ya} kopya, ` +
  `doluluk okuma ${doluluk(OKUMA, ok).toFixed(3)}, yazma ${doluluk(YAZMA, ya).toFixed(3)}`);
console.log(`bolmenin bedeli = ${ok + ya - tek} kopya\n`);

console.log("yazma carpani   tek havuz doluluk   ayri havuz okuma   ayri havuz yazma   yazma havuzu tasti");
for (const c of [1, 2, 3, 4]) {
  const y = YAZMA * c;
  console.log(`${`x${c}`.padStart(13)}   ${doluluk(OKUMA + y, tek).toFixed(3).padStart(17)}   ` +
    `${doluluk(OKUMA, ok).toFixed(3).padStart(16)}   ${doluluk(y, ya).toFixed(3).padStart(16)}   ` +
    `${String(y / ya > GUVENLI).padStart(18)}`);
}

console.log(`\nkarar hizi: katman 4'te ${((OKUMA + YAZMA) / 10).toFixed(2)} karar/s (Y4 = 10), ` +
  `katman 7'de ${(OKUMA + YAZMA).toFixed(2)} karar/s`);
console.log(`duyarlilik: yazma hizi ${(GUVENLI * ya).toFixed(0)} istek/s'yi gecince yazma havuzu ` +
  `${ya + 1} kopyaya cikar, okuma havuzu degismez`);
tek havuz : 3 kopya, doluluk 0.428
ayri havuz: okuma 3 + yazma 1 = 4 kopya, doluluk okuma 0.347, yazma 0.243
bolmenin bedeli = 1 kopya

yazma carpani   tek havuz doluluk   ayri havuz okuma   ayri havuz yazma   yazma havuzu tasti
           x1               0.428              0.347              0.243                false
           x2               0.509              0.347              0.486                false
           x3               0.590              0.347              0.729                 true
           x4               0.671              0.347              0.972                 true

karar hizi: katman 4'te 51.39 karar/s (Y4 = 10), katman 7'de 513.89 karar/s
duyarlilik: yazma hizi 200 istek/s'yi gecince yazma havuzu 2 kopyaya cikar, okuma havuzu degismez

İlk iki satır bölmenin faturasını hesap olarak veriyor: tek havuz 3 kopya ister, ayrı havuzlar 3 artı 1 eder 4. Bölmek bir kopyaya mal oluyor, çünkü her havuz kendi hızına göre ayrı ayrı yukarı yuvarlanır ve yazma havuzunun 97,22 istek/s’si bir kopyanın 200 istek/s’lik payının yarısını boş bırakır. İçerik farkındalıklı yönlendirmenin kendi başına bir kapasite kazancı yoktur; tersine, kaynak ister.

Karşılığında aldığı şey ikinci tablodadır. Taşıyıcıdan gelen durum olayı akışı üç katına çıktığında — bir aktarma merkezi birikmiş taramaları toptan gönderdiğinde — tek havuzun doluluğu 0,428’den 0,590’a çıkıyor ve bu artış takip sorgularını da etkiler, çünkü aynı kopyalar ikisine de hizmet ediyor. Ayrı havuzlarda okuma doluluğu her çarpanda 0,347’de sabit kalıyor; taşma yalnız yazma havuzunda oluyor ve orada da üçüncü satırdan itibaren o havuzun kopya sayısını artırmak yeterli oluyor. Bölmenin satın aldığı şey kapasite değil, yalıtımdır: bir akışın taşması ötekinin doluluğunu değiştirmiyor.

Son satırlar dersin fiyat etiketini K01 ölçeğine taşıyor. Katman 4’te dengeleyici saniyede 51,39 karar verirdi; katman 7’de 513,89 karar verir. On kat karar, bir kopya fazla ve bir akış yalıtımı — üçü aynı kararın parçalarıdır.

Özet

  • Katman 7 dengeleme kararı her istek için yeniden verir ve kararı isteğin içeriğine bakarak verir; kopyayla kurulan bağlantı istemciyle kurulan bağlantıdan ayrıdır.
  • Aynı istemci karışımında istek sapması katman 4’te 4,00, katman 7’de 1,07; kalan sapma 63 isteğin dörde tam bölünememesinden gelir.
  • Yolu okumak akışları ayrı havuzlara bölmeyi olanaklı kılıyor: okuma kopyaları 17’şer takip, yazma kopyası 12 olay isteği aldı ve çapraz sızma olmadı.
  • Dengeleyici altı dış bağlantıyı dört iç bağlantıya indirdi; kopyaların gördüğü bağlantı sayısı istemci sayısından bağımsız hâle geldi.
  • Bedel karar sayısıdır: 6 yerine 63, K01 ölçeğinde 51,39 yerine 513,89 karar/s; ayrıca isteğin ayrıştırılması dengeleyiciyi başlık, tampon ve yeniden gönderme kararlarının sahibi yapar.
  • Akışları bölmek bir kopyaya mal oluyor (3 yerine 4) ama yalıtım satın alıyor: yazma akışı üç katına çıktığında tek havuzun doluluğu 0,428’den 0,590’a çıkarken ayrı havuzlarda okuma doluluğu 0,347’de kalıyor.

Sonraki Adım

Buraya kadarki üç ders dağıtım kuralını sabit tuttu: kopyalar sırayla seçildi. Sıralı kural kopyaların birbirinin eşi olduğunu ve her isteğin aynı işi yaptığını varsayar. İkisi de her zaman doğru değildir — bir kopya yavaşlamış olabilir, bir istek ötekinden on kat pahalı olabilir, ve bir isteğin hangi kopyaya düştüğü önbelleğin işine yarayacak biçimde kararlı tutulmak istenebilir. Sonraki ders kuralın kendisini değiştirir: sıralı dağıtımı, açık bağlantı sayısına bakan bir kuralla ve anahtara bakan bir kuralla aynı yük altında karşılaştırır, üçüncüsünün kopya sayısı değiştiğinde kaç anahtarın yer değiştirdiğ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