İçeriğe geç
academia.sh

Ders 15 / 16

Elçi ve Yan Araba Kalıpları

Ortak işin ayrı bir yardımcı sürece verilmesi: elçinin dış sözleşmeyi servis kodundan ayırdığının iki taşıyıcı sürümüyle ölçülmesi, yan arabanın çalışma zamanı bağımsızlığı ile yeniden yayınlanan birim sayısına etkisi ve süreç sayısının ödettiği yerel durak.

İçindekiler

Önceki ders kenar birimi sayısını 1’den 3’e çıkardı ve bir sorun bıraktı: aynı ortak kod her birimde tekrarlanıyor. Ortak kodu paylaşmanın bilinen yolu onu bir kitaplığa almaktır, ama kitaplık iki şey dayatır — her birim aynı çalışma zamanında olmalı ve kitaplığın sürümü değiştiğinde her birim yeniden yapılıp yayınlanmalıdır.

Başka bir yol, ortak işi ayrı bir sürece vermektir. Yan araba (sidecar), ana süreçle aynı dağıtım biriminde koşan ve onun ortak işlerini üstlenen yardımcı süreçtir. Elçi (ambassador), aynı yerleşimin giden çağrılara uygulanmış hâlidir: servis yerel bir adrese çağrı yapar, elçi dış hedefi bulur ve dış sözleşmeye çevirir. İkisi de kalıp olarak yeni değildir — Alan Odaklı Tasarım kursundaki bağlantı noktaları ve bağdaştırıcılar dersi aynı ayrımı kod düzeyinde kurmuştu. Burada ele alınan şey kalıbın kendisi değil, dağıtım birimi düzeyindeki karşılığı ve o karşılığın ödettiği sayılar.

Elçi: Dış Sözleşmenin Servis Kodundan Ayrılması

Teslimat operasyonu bağlamı gönderi durumunu dış bir taşıyıcıdan alır. Taşıyıcı aynı bilgiyi iki ayrı sözleşmeyle sunuyor; düzenek ikisini de gerçek bir süreç olarak koşturur.

// elci/tasiyici.mjs — dis tasiyici servisi 8481: ayni bilgiyi iki ayri sozlesmeyle sunar
import { createServer } from "node:http";

const KAYIT = { "TR-4821": { kod: "OFD", bolge: "35", zaman: "2026-03-11T08:24:00Z" } };

createServer((istek, yanit) => {
  const [surum, , kuyruk] = istek.url.split("/").filter(Boolean);
  const no = surum === "v1" ? new URL(istek.url, "http://y").searchParams.get("no") : kuyruk;
  const k = KAYIT[no];
  const govde = !k ? { error: "not_found" }
    : surum === "v1" ? { code: k.kod, zone: k.bolge, ts: Date.parse(k.zaman) / 1000 }
      : { state: "out_for_delivery", region: { code: k.bolge }, updatedAt: k.zaman };
  yanit.writeHead(k ? 200 : 404, { "Content-Type": "application/json" });
  yanit.end(JSON.stringify(govde));
}).listen(8481, "127.0.0.1", () => console.log("tasiyici 127.0.0.1:8481"));

Elçi ayrı bir süreçtir ve hangi sözleşmeye konuşacağını başlatma argümanından alır. Yaptığı iş üç parçadır: dış yolu kurmak, dış gövdeyi iç biçime çevirmek ve dış çağrıyı ölçmek.

// elci/elci.mjs — elci sureci: yerel adresi dis sozlesmeye cevirir. Kullanim: node elci/elci.mjs v1 8480
import { createServer } from "node:http";

const [surum, port] = [process.argv[2], Number(process.argv[3])];
const DURUM_ADI = { OFD: "dagitimda", out_for_delivery: "dagitimda" };
let son = { yol: "-", bayt: 0 };                   // en son dis cagrinin yolu ve govde bayti

const disYol = (no) => (surum === "v1" ? `/v1/durum?no=${no}` : `/v2/status/${no}`);
const cevir = (d) => (surum === "v1"
  ? { durum: DURUM_ADI[d.code], bolge: d.zone, guncellenme: new Date(d.ts * 1000).toISOString() }
  : { durum: DURUM_ADI[d.state], bolge: d.region.code,
    guncellenme: new Date(d.updatedAt).toISOString() });

createServer(async (istek, yanit) => {
  const [kok, no] = istek.url.split("/").filter(Boolean);
  if (kok === "sayac") {
    yanit.writeHead(200, { "Content-Type": "application/json" });
    return yanit.end(JSON.stringify({ surum, ...son }));
  }
  const yol = disYol(no);
  const y = await fetch(`http://127.0.0.1:8481${yol}`);
  const metin = await y.text();
  son = { yol, bayt: Buffer.byteLength(metin) };
  const govde = y.ok ? cevir(JSON.parse(metin)) : { hata: "bulunamadi" };
  yanit.writeHead(y.ok ? 200 : 404, { "Content-Type": "application/json" });
  yanit.end(JSON.stringify({ takipNo: no, ...govde }));
}).listen(port, "127.0.0.1", () => console.log(`elci-${surum} 127.0.0.1:${port}`));

Ölçüm betiği servis kodunun yerine geçer: iki elçiye de aynı yerel adresi, aynı yolu ve aynı isteği verir. Servis kodunun tamamı tek satırdır ve içinde dış adres, dış yol ya da dış alan adı geçmez.

// elci/olc.mjs — servis kodu iki elciye ayni istegi yapar; disa cikan sozlesme farkli
const al = async (url) => {
  const y = await fetch(url);
  const metin = await y.text();
  return { bayt: Buffer.byteLength(metin), metin };
};

// Servis kodunun tamami: yerel bir adrese sabit bir yol ile istek yapar.
const servisIstegi = (elciPort) => `http://127.0.0.1:${elciPort}/durum/TR-4821`;

const YERLESIM = [["v1", 8480], ["v2", 8489]];
const sonuc = [];
for (const [surum, port] of YERLESIM) {
  const yanit = await al(servisIstegi(port));
  const sayac = JSON.parse((await al(`http://127.0.0.1:${port}/sayac`)).metin);
  sonuc.push({ surum, istek: "/durum/TR-4821", yanitBayt: yanit.bayt, yanit: yanit.metin,
    disYol: sayac.yol, disBayt: sayac.bayt });
}

console.log(`${"elci".padEnd(6)}${"servis istegi".padEnd(17)}${"yanit B".padStart(8)}` +
  `${"dis bayt".padStart(10)}  dis yol`);
for (const s of sonuc) {
  console.log(`${s.surum.padEnd(6)}${s.istek.padEnd(17)}${String(s.yanitBayt).padStart(8)}` +
    `${String(s.disBayt).padStart(10)}  ${s.disYol}`);
}
const ayni = new Set(sonuc.map((s) => s.yanit));
console.log(`\nservise donen govde (v1): ${sonuc[0].yanit}`);
console.log(`servise donen govde (v2): ${sonuc[1].yanit}`);
console.log(`servisin gordugu sozlesme = ${ayni.size}, dis sozlesme = ${sonuc.length}`);
console.log(`servis kodunda dis adres = 0, dis sozlesme degisince duzenlenen servis dosyasi = 0`);
node elci/tasiyici.mjs & t=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:8481/v1/durum?no=TR-4821
node elci/elci.mjs v1 8480 & a=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:8480/sayac
node elci/elci.mjs v2 8489 & b=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:8489/sayac
node elci/olc.mjs
kill $t $a $b
tasiyici 127.0.0.1:8481
elci-v1 127.0.0.1:8480
elci-v2 127.0.0.1:8489
elci  servis istegi     yanit B  dis bayt  dis yol
v1    /durum/TR-4821         95        42  /v1/durum?no=TR-4821
v2    /durum/TR-4821         95        86  /v2/status/TR-4821

servise donen govde (v1): {"takipNo":"TR-4821","durum":"dagitimda","bolge":"35","guncellenme":"2026-03-11T08:24:00.000Z"}
servise donen govde (v2): {"takipNo":"TR-4821","durum":"dagitimda","bolge":"35","guncellenme":"2026-03-11T08:24:00.000Z"}
servisin gordugu sozlesme = 1, dis sozlesme = 2
servis kodunda dis adres = 0, dis sozlesme degisince duzenlenen servis dosyasi = 0

Ölçümün taşıdığı sayı son satırlardadır: dış sözleşme 2, servisin gördüğü sözleşme 1. İki elçiye giden istek de aynı yola gitti ve iki elçiden dönen gövde birebir aynı 95 bayt. Dış tarafta ise yollar da baytlar da farklı: v1 çağrısı 42 bayt, v2 çağrısı 86 bayt taşıyor, yani aynı üç bilgi için ikinci sözleşme 2,05 kat gövde kullanıyor.

Sayaç bir denetim aracı olarak da işe yarıyor. Çevirinin eksik kalması, örneğin zaman damgasının bir sözleşmede olduğu gibi bırakılması, servisin gordugu sozlesme sayısını 1 yerine 2 yapardı. Elçinin işi “isteği iletmek” değil, iç sözleşmeyi tek tutmaktır ve bu sayı onun ölçüsüdür.

Yan Araba: Ortak Yeteneğin Dağıtım Birimine Bağlanması

İkinci ölçüm süreç içi bir modeldir: gerçek dağıtım aracı, kapsayıcı ya da yayınlama yoktur. Model bu kursta oluşan altı dağıtım birimini sayar — üç servis (takip, olay, ücret) ve önceki dersin eklediği üç kenar birimi — ve ikisinin iki ayrı çalışma zamanında olduğunu varsayar.

// elci/birim.mjs — ortak yetenegin iki yerlesimi; dagitim birimi duzeyinde surec ici model
const BIRIM = {                                  // bu kursta olusan alti dagitim birimi
  takip: "A", olay: "A", ucret: "B",             // A ve B: iki ayri calisma zamani
  "kenar-mobil": "A", "kenar-web": "A", "kenar-panel": "B",
};
const YERLESIM = {
  "kitaplik": { ayriSurec: false, calismaZamani: ["A"] },
  "yan araba": { ayriSurec: true, calismaZamani: ["A", "B"] },
};

const olc = (y) => {
  const birim = Object.entries(BIRIM);
  const kullanabilen = birim.filter(([, z]) => y.calismaZamani.includes(z)).length;
  return {
    "ortak yetenegi kullanabilen birim": `${kullanabilen}/${birim.length}`,
    "surum degisince yeniden yapilan birim": y.ayriSurec ? 1 : kullanabilen,
    "yeniden yayinlanan servis kodu": y.ayriSurec ? 0 : kullanabilen,
    "dagitim birimi basina surec": y.ayriSurec ? 2 : 1,
    "toplam surec": birim.length * (y.ayriSurec ? 2 : 1),
    "kenar birimindeki yerel durak": y.ayriSurec ? 2 : 1,
  };
};

const ALAN = Object.keys(olc(YERLESIM["yan araba"]));
const olcum = Object.entries(YERLESIM).map(([ad, y]) => ({ ad, ...olc(y) }));
console.log(`dagitim birimi = ${Object.keys(BIRIM).length} ` +
  `(calisma zamani A: ${Object.values(BIRIM).filter((z) => z === "A").length}, ` +
  `B: ${Object.values(BIRIM).filter((z) => z === "B").length})`);
console.log(`${"olcu".padEnd(38)}${olcum.map((o) => o.ad.padStart(12)).join("")}`);
for (const a of ALAN) console.log(`${a.padEnd(38)}${olcum.map((o) => String(o[a]).padStart(12)).join("")}`);

// K01 Kabaca Buyukluk Hesabi: uctaki tepe 513.89 istek/s (hesap sinifi)
const UC_TEPE = 513.89;
console.log("");
for (const o of olcum) {
  const durak = 1 + o["kenar birimindeki yerel durak"];      // ag gecidi duragi + kenar duraklari
  console.log(`${o.ad.padEnd(10)} durak ${durak} -> durak-istek/s = ${(UC_TEPE * durak).toFixed(2)}`);
}
dagitim birimi = 6 (calisma zamani A: 4, B: 2)
olcu                                      kitaplik   yan araba
ortak yetenegi kullanabilen birim              4/6         6/6
surum degisince yeniden yapilan birim            4           1
yeniden yayinlanan servis kodu                   4           0
dagitim birimi basina surec                      1           2
toplam surec                                     6          12
kenar birimindeki yerel durak                    1           2

kitaplik   durak 2 -> durak-istek/s = 1027.78
yan araba  durak 3 -> durak-istek/s = 1541.67

Sayıların Okunması

İlk satır kitaplık yerleşiminin sessiz kısıtını gösteriyor: ortak yeteneği altı birimin yalnız dördü kullanabiliyor, çünkü ikisi başka bir çalışma zamanında. Bu birimler ya yeteneği kendileri yeniden yazar ya da onsuz kalır; iki durumda da “ortak yetenek” sıfatı yanlıştır. Yan araba yerleşiminde sayı 6/6 olur, çünkü iki süreç arasındaki bağ bir işlev çağrısı değil bir yerel çağrıdır.

İkinci ve üçüncü satırlar yayınlama maliyetini ayırıyor. Ortak yeteneğin sürümü değiştiğinde kitaplık yerleşiminde dört birim yeniden yapılıp yayınlanır ve dört servis kodu yeniden yayınlanmış olur. Yan araba yerleşiminde yeniden yapılan birim 1, yeniden yayınlanan servis kodu 0’dır: servislerin yapıtı değişmez. Bu, önceki derslerin “tekrarlanan satır” ölçüsünün dağıtım düzeyindeki karşılığıdır — orada aynı kodun kaç dosyada durduğu, burada aynı değişikliğin kaç yapıtı yenilettiği sayılıyor.

Son üç satır bedeli veriyor ve bedel ucuz değil. Dağıtım birimi başına süreç 1’den 2’ye, toplam süreç 6’dan 12’ye çıkıyor. Her kenar birimi içinde bir yerel durak daha oluşuyor ve bu, ilk dersin durak-istek/s ölçüsünü büyütüyor.

Hesaba Dönüş

İlk ders K01’in uçtaki tepe hızını (513,89 istek/s) alıp ağ geçidiyle birlikte her isteğin iki durağa dokunduğunu ve durak-istek hızının 1027,78’e çıktığını hesaplamıştı. Yan araba üçüncü durağı ekliyor: 513,89 × 3 = 1541,67 durak-istek/s. Sayı K01’in hiçbir varsayımını değiştirmiyor — günlük sorgu, gövde boyutu ve tepe çarpanı aynı kalıyor — ama aynı istek hızının sistemde kaç yerde işlendiğini üç katına çıkarıyor.

Bu, süre ölçüsü değildir; bu makinede ölçülmüş bir gecikme yoktur ve yazılmaz. Söylediği şey şudur: bir yardımcı süreç kararı, hiçbir varsayımı değiştirmeden K01’in tepe hızını sistemin üç ayrı yerinde birden görünür kılar. Kararın savunması bu yüzden “ilgileri ayırıyor” cümlesiyle yazılamaz; yanına 6/6, 1, 0, 12 ve 1541,67 sayıları yazılır.

Özet

  • Yan araba ana süreçle aynı dağıtım biriminde koşan yardımcı süreçtir; elçi aynı yerleşimin giden çağrılara uygulanmış hâlidir ve dış hedefi ile dış sözleşmeyi üstlenir.
  • Elçi ölçümünde dış sözleşme 2, servisin gördüğü sözleşme 1 çıktı: iki elçi de aynı 95 baytlık gövdeyi döndürdü, dışarıda 42 ve 86 bayt taşındı.
  • Servis kodunda dış adres 0 ve dış sözleşme değiştiğinde düzenlenen servis dosyası 0; eksik bir çeviri servisin gordugu sozlesme sayısını 1’den 2’ye çıkararak kendini belli eder.
  • Kitaplık yerleşiminde ortak yeteneği altı birimin dördü kullanabiliyor ve sürüm değiştiğinde dört servis kodu yeniden yayınlanıyor; yan arabada 6/6 kullanabiliyor, yeniden yapılan birim 1, yeniden yayınlanan servis kodu 0.
  • Ödenen sayılar: dağıtım birimi başına süreç 1’den 2’ye, toplam süreç 6’dan 12’ye çıkıyor.
  • K01’e dönüş: uçtaki 513,89 istek/s üçüncü durakla birlikte 1541,67 durak-istek/s’ye çıkıyor; hiçbir varsayım değişmiyor, aynı hız üç yerde işleniyor.

Sonraki Adım

Beş ders boyunca kenara yerleştirilen her iş bir kazanç ve bir bedelle yazıldı, ama hepsinin ortak bir kabulü vardı: kenara ulaşan istek işlenmeye değer bir istektir. Oysa uçtaki tepe hızın bir bölümü hiçbir zaman yanıtlanmayacak isteklerden oluşur — eksik alanlı gövdeler, tanınmayan yollar, sınırı aşan boyutlar, biçimsiz belirteçler. Bu istekler uygulamaya ulaştığında uygulamanın kaynaklarını, ulaşmadan önce durdurulduğunda kenarın kaynaklarını harcar. Son ders doğrulamayı ayrı bir katmana yalıtan kapı bekçisi kalıbını ele alır ve geçersiz isteğin uygulamaya ulaşma oranını ölçüp K01’in uçtaki tepe hızına uygular.

İ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