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.