İçeriğe geç
academia.sh

Ders 13 / 18

Tek Parça Mimari

Aynı kitaplığın iki bağlamının tek dağıtım birimi olarak yayınlanması: bir teklif isteğinin geçtiği sınır sayısının, serileştirme noktasının ve sınırdan geçen baytın sayılması, tek dosyalık bir değişikliğin yeniden yayınlattığı dosya sayısının ölçülmesi ve süreci sonlandıran bir kusurun her iki bağlamı birden düşürdüğünün gösterilmesi.

İçindekiler

Önceki konu birimlerin birbiriyle nasıl konuştuğunu ele aldı. Her etkileşim biçeminde aynı üç şey ölçüldü: birimin karşı tarafı bilme yükümlülüğü, bağlanma yönü ve sınırdan geçen veri. Ancak bütün bu ölçümler bir soruyu sormadan geçti. İstemci ile sunucu, yayımcı ile abone, boru ile süzgeç — bunların hepsi tek bir çalışan programın içinde de kurulabilir. Yayımla–abone ol düzeni için ikinci bir süreç gerekmez; bir olay veriyolu nesnesi yeter.

Bu konu o sorunun kendisini sorar: yazılım kaç ayrı parça olarak yayınlanır? Cevap etkileşim biçeminden bağımsızdır ve kendi ölçüleri vardır. Bir dağıtım birimi (deployment unit) birlikte yayınlanmak zorunda olan dosya kümesidir; bir sürüm çıkarıldığında bu kümenin tamamı aynı anda yerine geçer. Tek parça mimari (monolithic architecture) bütün bağlamları tek bir dağıtım biriminde tutan biçemdir: kaç modül olduğu değişir, dağıtım birimi sayısı 1’dir.

İki Bağlam, Tek Süreç

Kitaplığın iki sınırlı bağlamı var. Ücretlendirme bağlamında gönderi fiyatlanabilir bir birimdir; teslimat operasyonu bağlamında rotası ve taşıyıcısı olan fiziksel bir pakettir. Her ikisi de derli biçimde şöyledir.

// ortak/ucret.mjs — ucretlendirme baglami: gonderi fiyatlanabilir birimdir
export const TARIFE = { kademe: [[1, 3000], [5, 4800], [20, 9600]], bolge: { "34": 100, "06": 115, "35": 125 }, asgari: 2500 };

export function taban(agirlik, bolge) {
  const kademe = TARIFE.kademe.find(([k]) => agirlik <= k) ?? [0, 9600];
  return Math.max(TARIFE.asgari, Math.round((kademe[1] * TARIFE.bolge[bolge]) / 100));
}

export function net(agirlik, bolge, sozlesmeOrani) {
  const t = taban(agirlik, bolge);
  return t - Math.round(t * Math.min(sozlesmeOrani, 0.4));
}
// ortak/operasyon.mjs — teslimat operasyonu baglami: gonderi rotali fiziksel pakettir
export const AGAC = { "34": ["34"], "06": ["34", "06"], "35": ["34", "41", "35"] };

export function rota(bolge) { return AGAC[bolge] ?? ["34"]; }

export function tasiyici(bolge) { return rota(bolge).length > 2 ? "MT" : "AN"; }

export function teslimGunu(bolge) { return rota(bolge).length; }

Sınır geçişlerini saymak için birimlerin dış yüzü sarılır. Bu sarmalayıcı kurs boyunca aynı kalır: hangi biçem ölçülüyorsa geçişleri o toplar.

// ortak/sar.mjs — bir modulun dis yuzunu sarar; her cagri birim adiyla kayda gecer
export function sar(ad, modul, kayit) {
  const yuz = {};
  for (const [k, v] of Object.entries(modul)) {
    yuz[k] = typeof v === "function" ? (...a) => { kayit.push(`${ad}.${k}`); return v(...a); } : v;
  }
  return yuz;
}

Teklif isteği iki bağlama birden dokunur: net ücret ücretlendirmeden, teslim günü ve taşıyıcı operasyondan gelir. Tek parça düzende bu birleştirme bir fonksiyon çağrısıdır.

// tek-parca/uygulama.mjs — iki baglam ayni surecte, ayni bellekte, tek dagitim birimi
import * as ucretModul from "../ortak/ucret.mjs";
import * as operasyonModul from "../ortak/operasyon.mjs";
import { sar } from "../ortak/sar.mjs";

export function uygulama(kayit) {
  const ucret = sar("ucret", ucretModul, kayit);
  const operasyon = sar("operasyon", operasyonModul, kayit);
  return {
    teklif(g) {
      kayit.push("uygulama.teklif");
      return {
        id: g.id,
        net: ucret.net(g.agirlik, g.bolge, g.oran),
        gun: operasyon.teslimGunu(g.bolge),
        tasiyici: operasyon.tasiyici(g.bolge),
      };
    },
  };
}

İsteğin ve Yayının Ölçülmesi

Ölçüm iki cinsten sayı çıkarır. Birincisi koşumdan gelir: bir isteğin dokunduğu birim, geçtiği sınır, serileştirme noktası ve sınırdan geçen bayt. İkincisi dosyalardan gelir: dağıtım biriminin ithal kapanışı, yani birlikte yayınlanmak zorunda olan dosya kümesi.

// tek-parca/olc.mjs — bir istegin dokundugu birim, gectigi sinir, yayin kumesi ve komsu gereksinimi
import { readFileSync } from "node:fs";
import { dirname, join, normalize } from "node:path";
import { uygulama } from "./uygulama.mjs";

const kayit = [];
const cikti = uygulama(kayit).teklif({ id: "G1", agirlik: 4, bolge: "35", oran: 0.15 });
const birim = new Set(kayit.map((c) => c.split(".")[0]));
console.log(`teklif ciktisi = ${JSON.stringify(cikti)}`);
console.log(`sinir gecisi = ${kayit.length} (${kayit.join(" -> ")})`);
console.log(`dokunulan birim = ${birim.size}, serilestirme noktasi = 0, sinirdan gecen bayt = 0`);

const ITHAL = /from\s+"(\.[^"]+)"/g;
function kapanis(giris) {
  const yigit = [giris], gorulen = new Set();
  while (yigit.length > 0) {
    const d = normalize(yigit.pop());
    if (gorulen.has(d)) continue;
    gorulen.add(d);
    for (const [, y] of readFileSync(d, "utf8").matchAll(ITHAL)) yigit.push(join(dirname(d), y));
  }
  return [...gorulen].sort();
}

const kume = kapanis("tek-parca/uygulama.mjs");
console.log(`dagitim birimi = 1, birimin kapanisi = ${kume.length} dosya (${kume.join(", ")})`);
console.log(`ucret.mjs degisirse: degisen dosya = 1, yeniden yayinlanan dosya = ${kume.length}`);
for (const g of ["ortak/ucret.mjs", "ortak/operasyon.mjs"]) {
  console.log(`${g} tek basina kosmak icin gereken komsu = ${kapanis(g).length - 1}`);
}
node tek-parca/olc.mjs
teklif ciktisi = {"id":"G1","net":5100,"gun":3,"tasiyici":"MT"}
sinir gecisi = 4 (uygulama.teklif -> ucret.net -> operasyon.teslimGunu -> operasyon.tasiyici)
dokunulan birim = 3, serilestirme noktasi = 0, sinirdan gecen bayt = 0
dagitim birimi = 1, birimin kapanisi = 4 dosya (ortak/operasyon.mjs, ortak/sar.mjs, ortak/ucret.mjs, tek-parca/uygulama.mjs)
ucret.mjs degisirse: degisen dosya = 1, yeniden yayinlanan dosya = 4
ortak/ucret.mjs tek basina kosmak icin gereken komsu = 0
ortak/operasyon.mjs tek basina kosmak icin gereken komsu = 0

Sayıların Okunması

Dört sınır geçişi var ve dördü de fonksiyon çağrısıdır. Serileştirme noktası 0, sınırdan geçen bayt 0: g nesnesi ücretlendirmeye kopyalanmadan, aynı bellekteki başvurusuyla geçti. Bu biçemin en somut kazancıdır. Sınırdan geçen veri için bir biçim kararlaştırılmaz, sürüm uyumluluğu tartışılmaz, dönüştürme noktası tanımlanmaz — çünkü dönüştürme yoktur.

İkinci sayı çifti biçemin sınırını gösterir. Ücretlendirme kuralında bir satır değişirse değişen dosya 1’dir, ama yeniden yayınlanan dosya 4’tür. Ücretlendirmenin asgari ücretini düzelten bir sürüm, operasyon bağlamının kodunu da yeniden yayına sokar. Bu, kod düzeyinde bir bağlaşım değildir; iki bağlam birbirini ithal etmiyor. Yayın düzeyinde bir bağlaşımdır ve kaynağı biçemin kendisidir.

Son iki satır önemli bir ayrımı ortaya koyar. Her iki bağlam tek başına sınanabilir: hiçbiri ötekinin dosyasına ihtiyaç duymuyor, gereken komşu sayısı 0. Yani tek parça mimari bağımsız sınanabilirliği engellemez; engellediği bağımsız yayındır. Bu iki şeyin karıştırılması, biçem tartışmalarında en sık yapılan hatadır.

Paylaşılan Kader

Tek parça biçemin ikinci sınırı arıza davranışındadır. İki bağlam aynı süreçte koştuğu için süreci sonlandıran bir kusur ikisini birden durdurur. İstek başına yakalama koymak yetmez: yakalama yalnız kendi çağrı yığıtındaki hatayı tutar, sonraya bırakılmış bir görevden yükselen hatayı tutmaz.

// tek-parca/ariza.mjs — istek basina yakalama varken bile sureci sonlandiran kusur
import * as ucret from "../ortak/ucret.mjs";
import * as operasyon from "../ortak/operasyon.mjs";

const ISTEK = [
  { birim: "ucret", id: "G1" }, { birim: "operasyon", id: "G2" },
  { birim: "ucret", id: "G3", kusurlu: true }, { birim: "operasyon", id: "G4" },
  { birim: "ucret", id: "G5" }, { birim: "operasyon", id: "G6" },
];

let yanitlanan = 0;
process.on("exit", () => {
  console.log(`yanitlanan istek = ${yanitlanan} / ${ISTEK.length}`);
  console.log(`ayakta kalan birim = ${yanitlanan === ISTEK.length ? 2 : 0} / 2`);
});

for (const istek of ISTEK) {
  try {
    if (istek.birim === "ucret") {
      if (istek.kusurlu) setTimeout(() => { throw new RangeError(`tarife kademesi yok: ${istek.id}`); });
      console.log(`${istek.id} ucret  = ${ucret.net(4, "35", 0.15)}`);
    } else {
      console.log(`${istek.id} rota   = ${operasyon.rota("35").join("-")}`);
    }
    yanitlanan += 1;
  } catch (e) {
    console.log(`${istek.id} yakalandi: ${e.message}`);
  }
  await new Promise((c) => setTimeout(c, 5));
}

Yığıt izi standart hata akışına yazılır; aşağıdaki komut onu ayırıp yalnız sayılan çıktıyı ve çıkış kodunu bırakır.

node tek-parca/ariza.mjs 2>/dev/null; echo "cikis kodu = $?"
G1 ucret  = 5100
G2 rota   = 34-41-35
G3 ucret  = 5100
yanitlanan istek = 3 / 6
ayakta kalan birim = 0 / 2
cikis kodu = 1

Altı isteğin üçü yanıtlandı. Kusur ücretlendirme bağlamındaydı, ama G4 ve G6 operasyon isteklerinin ikisi de yanıtsız kaldı; ayakta kalan birim 0. Arıza yalıtımı bu biçemde ölçülemez bir nitelik değildir, ölçülür ve sonucu sıfırdır.

Biçemin Bıraktığı Fatura

Sayılar bir kalite niteliği ödünleşimidir ve iki yönü de aynı derste görünüyor. Kazanç tarafında: sınırdan geçen bayt 0, serileştirme noktası 0, bir isteğin uçtan uca izlenmesi için bakılacak süreç sayısı 1. Kayıp tarafında: bir dosyalık değişiklik için yeniden yayınlanan dosya 4, altı istekte ayakta kalan birim 0.

Bu ödünleşimin yönü ekip ve sürüm sıklığıyla değişir. İki bağlamı aynı ekip aynı takvimde yayınlıyorsa “yeniden yayınlanan dosya 4” bir maliyet değildir; yayın zaten birlikte yapılacaktı. Bağlamlar ayrı takvimlere geçtiğinde aynı sayı doğrudan bir bekleme süresine dönüşür: ücretlendirme sürümü, operasyon tarafının kararlı olmasını beklemek zorundadır.

Ölçek sınırı da aynı yerden çıkar. Süreç tek olduğu için kopyalama ancak bütün olarak yapılır: ücretlendirme isteklerinin sayısı arttığında operasyon kodu da onunla birlikte çoğaltılır. Kopyalanan birim sayısı 1 olduğu sürece hangi bağlamın yüklendiği bilgisi kopyalama kararına giremez.

Özet

  • Dağıtım birimi birlikte yayınlanmak zorunda olan dosya kümesidir; tek parça mimari bütün bağlamları tek dağıtım biriminde tutan biçemdir.
  • Teklif isteği 3 birime dokundu ve 4 sınırdan geçti; dördü de fonksiyon çağrısı olduğu için serileştirme noktası 0, sınırdan geçen bayt 0 ölçüldü.
  • Bir dosyada yapılan değişiklik 4 dosyayı yeniden yayınlattı; bu, kod bağlaşımı değil yayın bağlaşımıdır ve kaynağı biçemin kendisidir.
  • Her iki bağlam tek başına koşabildi (gereken komşu 0); biçem bağımsız sınanabilirliği değil bağımsız yayını engelliyor.
  • Süreci sonlandıran bir kusurda altı isteğin 3’ü yanıtlandı ve ayakta kalan birim 0 oldu; arıza yalıtımı ölçüldü ve sıfır çıktı.
  • Ödünleşimin yönü sabit değildir: bağlamlar aynı takvimde yayınlanıyorsa yayın bağlaşımı bedel değildir, ayrı takvimlere geçildiğinde bekleme süresine dönüşür.

Sonraki Adım

Yayın bağlaşımının bedeli iki bağlam ayrı takvimlere geçtiğinde ortaya çıkıyor. İlk çözüm denemesi bağlamları ayırmak değil, aralarına paylaşılan bir tümleştirme yüzeyi koymaktır: her bağlam kendi dağıtım birimine çıkar, ama ikisi de ortak bir kayıt biçimi ve ortak bir çağrı yolu üzerinden konuşur. Bu düzen kurum çapında yeniden kullanımı hedefler ve ölçüsü de oradan gelir: ortak biçimin alan sayısı, her sınır geçişindeki dönüştürme noktası sayısı ve ortak biçimde bir alan değiştiğinde birlikte yayınlanmak zorunda kalan servis sayısı. Sonraki ders bu üç sayıyı ölçer.

İ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