İçeriğe geç
academia.sh

Ders 06 / 10

Kurumsal Servis Veri Yolu

Aynı akış kümesinin ikili bağlarla ve merkezî bir noktayla kurulup karşılaştırılması: yazılan çevirici ve uyarlayıcı sayısı, bir kuralın kaç yerde geçtiği, merkezin işlediği ileti sayısı, merkez düştüğünde kopan akış oranı, yeni bir sistem eklemenin dokunduğu sahip sayısı ve merkezde biriken iş mantığının sahipliği.

İçindekiler

Önceki ders tek bir bağı dört kalıpla kurdu ve bir kalıbın neye mal olduğunu gösterdi. Kurumda ise her sistem her sistemle kendi seçtiği yoldan konuşuyor. Bu ders aynı veri akışı kümesini iki düzende kurar — her akış için doğrudan bir bağ, ya da bütün akışların geçtiği tek bir merkezî nokta — ve ikisini aynı ölçülerle karşılaştırır.

TM4 — kurum kurgudur, akış kümesi node içinde bir dizi olarak yazılır. Ölçülen her nicelik ya bu diziden ya çalışan süreçlerden ya da tümleştirme kodunun kendi metninden okunur.

TM5 — alıcı sistemler tek bir süreçte, her biri kendi portunda modellenir. Gerekçe: ölçülen nicelikler (yazılan birim, geçilen atlama, işlenen ileti, kopan akış) sistemlerin ayrı makinelerde durup durmamasından bağımsızdır; ayrı portlar sistem sınırını korumaya yeter.

TM6 — merkezî nokta bir ürün değil, bir konumdur. Ölçülen şey merkezin hangi yetenekleri taşıdığı değil, bütün akışların tek bir noktadan geçmesinin bağ sayısına, arıza yüzeyine ve sahipliğe ne yaptığıdır.

Akış Kümesi

Bölgesel kütüphane ağında altı sistem, dokuz veri akışı ve beş ayrı sahip vardır. Ödünç servisi her ödüncü dört sisteme bildirir, üyelik sistemi üye değişikliğini üç sisteme, katalog nüsha durumunu iki sisteme. Dokuz akış, altı sistemin kurabileceği otuz yönlü çiftin küçük bir alt kümesidir; graf seyrektir ve bu seyreklik ilerideki sayıların çoğunu belirler.

// kurum.mjs — kurgu kurum modeli: sistemler, sahipleri ve aralarindaki veri akislari
export const SAHIP = { katalog: "katalog birimi", odunc: "ic gelistirme",
  ucretlendirme: "ic gelistirme", uyelik: "uyelik birimi", raporlama: "yonetim birimi",
  "sube-arayuzu": "sube birimi", ayirtma: "sube birimi", merkez: "tumlestirme birimi" };
export const ALICI = ["ucretlendirme", "raporlama", "katalog", "sube-arayuzu", "odunc"];
export const AKIS = [                            // [kaynak, hedef]; her satir bir bag
  ["odunc", "ucretlendirme"], ["odunc", "raporlama"], ["odunc", "katalog"],
  ["odunc", "sube-arayuzu"], ["uyelik", "odunc"], ["uyelik", "ucretlendirme"],
  ["uyelik", "raporlama"], ["katalog", "sube-arayuzu"], ["katalog", "raporlama"],
];
export const YENI = [                            // yedinci sistem eklenince dogan akislar
  ["odunc", "ayirtma"], ["uyelik", "ayirtma"], ["katalog", "ayirtma"],
  ["ayirtma", "odunc"], ["ayirtma", "sube-arayuzu"],
];
export const derece = (a) => AKIS.filter((b) => b.includes(a)).length;

if (process.argv[1].endsWith("kurum.mjs")) {
  const s = [...new Set(AKIS.flat())];
  console.log(`${s.length} sistem, ${AKIS.length} akis, ${new Set(s.map((a) => SAHIP[a])).size} sahip; ` +
    `olasi yonlu cift ${s.length * (s.length - 1)}`);
  console.log(`en cok akisa dokunan sistem: ` +
    `${s.map((a) => `${a} ${derece(a)}`).sort((x, y) => y.split(" ")[1] - x.split(" ")[1])[0]}`);
  console.log(`yedinci sistem eklenince akis ${AKIS.length} -> ${AKIS.length + YENI.length}`);
}
6 sistem, 9 akis, 5 sahip; olasi yonlu cift 30
en cok akisa dokunan sistem: odunc 5
yedinci sistem eklenince akis 9 -> 14

Alıcı sistemler tek bir süreçte, beş ayrı portta durur ve aldıkları kaydı kendi kütüklerine yazarlar; iki düzenin sonucu bu kütüklerden karşılaştırılır.

// sistemler.mjs — bes alici sistem, her biri kendi portunda; <taban-port>; uzun omurlu surec
import { createServer } from "node:http";
import { appendFileSync } from "node:fs";
import { ALICI } from "./kurum.mjs";
const [taban] = process.argv.slice(2);
if (!taban) { console.log("kullanim: node sistemler.mjs <taban-port>"); process.exit(1); }
ALICI.forEach((ad, i) => createServer(async (istek, yanit) => {
  let s = "";
  for await (const p of istek) s += p;
  appendFileSync(`alinan-${ad}.log`, `${s}\n`);
  yanit.end("tamam");
}).listen(Number(taban) + i));

İkili Bağlar

İkili düzende her akış için bir çevirici yazılır ve çevirici kaynak sistemin içinde durur. Her sistemin kendi sözcük dağarcığı vardır: ödünç servisi uyeId der, üyelik sistemi uye_no, ücretlendirme uyeNo. Çevirici bu farkı kapatır ve hedefin beklediği alanları üretir. Dört kural (gecikme ücreti, rapor dönemi, şube bölgesi, üye sınıfı) çeviricilerin içinde çağrılır.

// ikili.mjs — ikili tumlestirme: her akis icin bir cevirici; cevirici kaynak sistemde durur
export const ucret = (g) => g * 5;                          // gecikme ucreti kurali
export const donem = (z) => z.slice(0, 7);                  // rapor donemi kurali
export const bolge = (k) => (k <= 2 ? "kuzey" : "guney");   // sube bolgesi kurali
export const sinif = (d) => (d === 1 ? "tam" : "kisitli");  // uye sinifi kurali
export const CEVIRICI = {
  "odunc->ucretlendirme": (o) => ({ uyeNo: o.uyeId, nusha: o.nushaId, borc: ucret(o.gecikmeGunu) }),
  "odunc->raporlama": (o) => ({ olay: "odunc", anahtar: o.nushaId, donem: donem(o.zaman) }),
  "odunc->katalog": (o) => ({ nushaId: o.nushaId, uygun: false }),
  "odunc->sube-arayuzu": (o) => ({ nusha: o.nushaId, bolge: bolge(o.subeKodu) }),
  "uyelik->odunc": (o) => ({ uyeId: o.uye_no, sinif: sinif(o.durum_kodu) }),
  "uyelik->ucretlendirme": (o) => ({ uyeNo: o.uye_no, sinif: sinif(o.durum_kodu) }),
  "uyelik->raporlama": (o) => ({ olay: "uye", anahtar: o.uye_no, donem: donem(o.zaman) }),
  "katalog->sube-arayuzu": (o) => ({ nusha: o.nushaId, bolge: bolge(o.subeKodu) }),
  "katalog->raporlama": (o) => ({ olay: "nusha", anahtar: o.nushaId, donem: donem(o.zaman) }),
};

Merkezî Nokta

Merkezî düzende üretici sistem kendi biçimini tek bir yere verir. Merkez o biçimi ortak biçime çevirir, akış tablosuna bakarak hedefleri bulur ve her hedef için o sistemin biçimine çevirip yollar. Sistem başına iki uyarlayıcı vardır: içeri ve dışarı. Dört kural artık tek bir yerde durur ve KURAL dizisi her kuralın işini kimin sahiplendiğini yazar.

// merkez.mjs — merkezi nokta; <port> <taban-port>; uzun omurlu surec
import { createServer } from "node:http";
import { AKIS, ALICI } from "./kurum.mjs";
const [port, taban] = process.argv.slice(2);
if (!taban) { console.log("kullanim: node merkez.mjs <port> <taban-port>"); process.exit(1); }
const ucret = (g) => g * 5, donem = (z) => z.slice(0, 7);   // kurallar merkezde toplandi
const bolge = (k) => (k <= 2 ? "kuzey" : "guney"), sinif = (d) => (d === 1 ? "tam" : "kisitli");
export const KURAL = [                           // merkezde duran kural ve isin asil sahibi
  { ad: "ucret", sahip: "ic gelistirme" }, { ad: "donem", sahip: "yonetim birimi" },
  { ad: "bolge", sahip: "sube birimi" }, { ad: "sinif", sahip: "uyelik birimi" },
];
const GIRIS = {                                  // sistem bicimi -> ortak bicim
  odunc: (o) => ({ tur: "odunc", uye: o.uyeId, nusha: o.nushaId, sube: o.subeKodu,
    gecikme: o.gecikmeGunu, zaman: o.zaman }),
  uyelik: (o) => ({ tur: "uye", uye: o.uye_no, durum: o.durum_kodu, zaman: o.zaman }),
  katalog: (o) => ({ tur: "nusha", nusha: o.nushaId, sube: o.subeKodu, zaman: o.zaman }),
};
const CIKIS = {                                  // ortak bicim -> sistem bicimi
  ucretlendirme: (c) => (c.tur === "odunc"
    ? { uyeNo: c.uye, nusha: c.nusha, borc: ucret(c.gecikme) }
    : { uyeNo: c.uye, sinif: sinif(c.durum) }),
  raporlama: (c) => ({ olay: c.tur, anahtar: c.nusha ?? c.uye, donem: donem(c.zaman) }),
  katalog: (c) => ({ nushaId: c.nusha, uygun: false }),
  "sube-arayuzu": (c) => ({ nusha: c.nusha, bolge: bolge(c.sube) }),
  odunc: (c) => ({ uyeId: c.uye, sinif: sinif(c.durum) }),
};
let gelen = 0, giden = 0;                        // merkezin isledigi ileti sayisi

createServer(async (istek, yanit) => {
  if (istek.url === "/sayac") { yanit.end(`${gelen} ${giden}`); return; }
  if (istek.url === "/kapat") { yanit.end("kapali"); process.exit(0); }
  let s = "";
  for await (const p of istek) s += p;
  const { kaynak, govde } = JSON.parse(s);
  gelen += 1;
  const ortak = GIRIS[kaynak](govde);
  for (const h of AKIS.filter((b) => b[0] === kaynak).map((b) => b[1])) {
    giden += 1;
    await fetch(`http://127.0.0.1:${Number(taban) + ALICI.indexOf(h)}/al`,
      { method: "POST", body: JSON.stringify(CIKIS[h](ortak)) });
  }
  yanit.end("tamam");
}).listen(Number(port));

Ölçüm

Araç aynı altı olayı önce ikili düzende, sonra merkezî düzende geçirir ve alıcı kütüklerini karşılaştırır. Yazılan birim sayısı ve kuralın geçtiği yer sayısı çalışan süreçlerden değil, iki tümleştirme dosyasının metninden okunur. Son adımda merkez kapatılır ve aynı altı olay yeniden gönderilir.

// olc.mjs — iki duzeni ayni akis kumesinde olcer; <taban-port> <merkez-port>
import { existsSync, readFileSync, rmSync } from "node:fs";
import { AKIS, ALICI, SAHIP, YENI } from "./kurum.mjs";
import { CEVIRICI } from "./ikili.mjs";
const [taban, mp] = process.argv.slice(2);
if (!mp) { console.log("kullanim: node olc.mjs <taban-port> <merkez-port>"); process.exit(1); }
const OLAY = [                                   // uretici sistemlerin kendi bicimindeki olaylari
  ["odunc", { uyeId: 7, nushaId: 401, subeKodu: 1, gecikmeGunu: 3, zaman: "2026-04-11" }],
  ["odunc", { uyeId: 9, nushaId: 402, subeKodu: 3, gecikmeGunu: 0, zaman: "2026-04-12" }],
  ["uyelik", { uye_no: 7, durum_kodu: 1, zaman: "2026-04-11" }],
  ["uyelik", { uye_no: 9, durum_kodu: 2, zaman: "2026-04-13" }],
  ["katalog", { nushaId: 401, subeKodu: 1, zaman: "2026-04-11" }],
  ["katalog", { nushaId: 403, subeKodu: 4, zaman: "2026-04-14" }],
];
const TESLIM = AKIS.length * 2;                  // her akis iki olay tasir
const sil = () => ALICI.forEach((a) => rmSync(`alinan-${a}.log`, { force: true }));
const topla = () => ALICI.flatMap((a) => existsSync(`alinan-${a}.log`)
  ? readFileSync(`alinan-${a}.log`, "utf8").split("\n").filter(Boolean).map((s) => `${a} ${s}`) : []);
const ikiliKos = async () => {                   // cevirici kaynak sistemde kosar, dogrudan yollar
  for (const [k, g] of OLAY) {
    for (const h of AKIS.filter((b) => b[0] === k).map((b) => b[1])) {
      await fetch(`http://127.0.0.1:${Number(taban) + ALICI.indexOf(h)}/al`,
        { method: "POST", body: JSON.stringify(CEVIRICI[`${k}->${h}`](g)) });
    }
  }
};
const merkezKos = async () => {                  // uretici yalniz kendi bicimini merkeze verir
  let kabul = 0;
  for (const [kaynak, govde] of OLAY) {
    try {
      await fetch(`http://127.0.0.1:${mp}/olay`, { method: "POST",
        body: JSON.stringify({ kaynak, govde }) });
      kabul += 1;
    } catch { kabul += 0; }                      // merkez kapaliysa uretici teslim edemez
  }
  return kabul;
};
sil(); await ikiliKos(); const a = topla().sort();
sil(); await merkezKos(); const b = topla().sort();
const [gelen, giden] = (await (await fetch(`http://127.0.0.1:${mp}/sayac`)).text()).split(" ").map(Number);
const mk = readFileSync("merkez.mjs", "utf8"), ik = readFileSync("ikili.mjs", "utf8");
const kesit = (m, ad) => m.split(`const ${ad} = {`)[1].split("\n};")[0];
const anahtar = (m) => m.match(/^ {2}"?[\w>-]+"?: \(/gm).length;   // kalip basina yazilan birim
const cagri = (m) => ["ucret", "donem", "bolge", "sinif"]          // kuralin gectigi yer sayisi
  .reduce((s, r) => s + (m.match(new RegExp(`\\b${r}\\(`, "g")) || []).length, 0);
const kural = mk.split("KURAL = [")[1].split("];")[0].match(/sahip: "([^"]+)"/g);
const kume = (l) => new Set(l).size;
const MD = ["ayirtma", "merkez"];                // merkezli duzende dokunulan birimler
const satir = (ad, x, y) => console.log(`${ad.padEnd(41)}${String(x).padStart(7)}${String(y).padStart(9)}`);

satir("olcu", "ikili", "merkezi");
satir("akis (bag) sayisi", AKIS.length, AKIS.length);
satir("yazilan cevirici / uyarlayici", anahtar(kesit(ik, "CEVIRICI")),
  anahtar(kesit(mk, "GIRIS")) + anahtar(kesit(mk, "CIKIS")));
satir("kuralin gectigi yer", cagri(ik), cagri(mk));
satir("teslim basina atlama", 1, 2);
satir("en yogun birimin isledigi ileti", 2, gelen + giden);
satir("bir birim dusunce kopan akis", Math.max(...AKIS.flat()
  .map((s) => AKIS.filter((b) => b.includes(s)).length)), AKIS.length);
satir("yedinci sistem: yazilacak birim", YENI.length, MD.length);
satir("yedinci sistem: dokunulan sahip", kume(YENI.map((b) => SAHIP[b[0]])),
  kume(MD.map((s) => SAHIP[s])));
satir("sahibi baska olan, merkezde duran kural", 0,
  kural.filter((s) => !s.includes(SAHIP.merkez)).length);
console.log(`iki duzenin teslimi: ${a.length}/${b.length} kayit, ` +
  `birebir ayni: ${JSON.stringify(a) === JSON.stringify(b) ? "evet" : "hayir"}`);

await fetch(`http://127.0.0.1:${mp}/kapat`).catch(() => 0);        // merkez kapatilir
sil(); const kabul = await merkezKos(); const dusukMerkez = topla().length;
sil(); await ikiliKos();
console.log(`merkez kapali: ${kabul}/${OLAY.length} olay kabul edildi, ` +
  `${dusukMerkez}/${TESLIM} teslim oldu; ikili duzen ayni anda ${topla().length}/${TESLIM} teslim etti`);
node sistemler.mjs 8991 & S=$!
node merkez.mjs 8990 8991 &
sleep 0.8
node olc.mjs 8991 8990
kill $S
olcu                                       ikili  merkezi
akis (bag) sayisi                              9        9
yazilan cevirici / uyarlayici                  9        8
kuralin gectigi yer                            8        5
teslim basina atlama                           1        2
en yogun birimin isledigi ileti                2       24
bir birim dusunce kopan akis                   5        9
yedinci sistem: yazilacak birim                5        2
yedinci sistem: dokunulan sahip                4        2
sahibi baska olan, merkezde duran kural        0        4
iki duzenin teslimi: 18/18 kayit, birebir ayni: evet
merkez kapali: 0/6 olay kabul edildi, 0/18 teslim oldu; ikili duzen ayni anda 18/18 teslim etti

Bağ Sayısı Ne Kadar Azalıyor

Son satırdan önceki karşılaştırma ölçümün geçerliliğini kuruyor: iki düzen de on sekiz kaydı teslim ediyor ve teslim edilen kayıtlar birebir aynı. Aradaki fark işin sonucunda değil, işin nerede durduğunda.

İkinci satır beklenen kazancı vermiyor. Dokuz akış için dokuz çevirici yerine sekiz uyarlayıcı yazılıyor; kazanç tek birim. Merkezî tümleştirmenin klasik gerekçesi bağ sayısının sistem sayısının karesi kadar artmasıdır, ama o gerekçe bütün sistemlerin birbiriyle konuştuğu varsayımına dayanır. Bu kurumda altı sistemin kurabileceği otuz yönlü çiftten yalnız dokuzu gerçekleşmiştir. Çevirici sayısı akış sayısıyla, uyarlayıcı sayısı sistem sayısıyla büyür; graf seyrek olduğu sürece ikisi birbirine yakın kalır ve merkezî düzenin bu kolondaki üstünlüğü küçüktür.

Asıl kazanç üçüncü satırdadır. Aynı dört kural ikili düzende sekiz yerde çağrılıyor, merkezde beş yerde. Rapor dönemi kuralı üç ayrı çeviricide geçiyor, çünkü raporlamaya veri gönderen üç sistem de o hesabı kendi kodunda yapmak zorunda. Dönem tanımı değişirse ikili düzende üç ayrı sistemin, merkezde tek bir birimin değişmesi gerekir. Merkezin sattığı şey bağ sayısı değil, tekrarın ortadan kalkmasıdır.

Merkezin Bedeli

Beşinci satır darboğazı sayıya çeviriyor. Altı olay için merkez yirmi dört ileti işliyor: altı giriş, on sekiz çıkış. İkili düzende en yoğun tek bağın işlediği ileti sayısı iki. Merkez, en yoğun ikili bağın on iki katı iş yapıyor ve bu oran akış sayısıyla büyüyor; üstüne teslim başına bir atlama ekliyor.

Altıncı satır arıza yüzeyini veriyor. İkili düzende en çok akışa dokunan sistem ödünç servisidir ve o düştüğünde dokuz akıştan beşi kopar. Merkez düştüğünde dokuzunun dokuzu kopar. Çıktının son satırı bunu ölçümle gösteriyor: merkez kapatıldığında altı olayın hiçbiri kabul edilmiyor ve on sekiz teslimden sıfırı gerçekleşiyor; aynı anda ikili düzen on sekiz teslimin on sekizini yapıyor. Ödünç servisi düştüğünde kurum ödünç veremediği için zaten durmuştur; merkez düştüğünde ise hiçbir iş yapmayan bir bileşen bütün kurumu durdurur. Tek arıza noktasının rahatsız edici yanı budur: iş değeri sıfır olan bir bileşenin arıza payı en yüksektir.

Yeni Sistem ve Merkezde Biriken Mantık

Yedinci ve sekizinci satırlar merkezî düzenin en güçlü savunmasıdır. Ayırtma sistemi eklendiğinde beş yeni akış doğuyor. İkili düzende bu beş akış için beş çevirici yazılır ve çeviriciler kaynak sistemlerin içinde durduğu için ödünç, üyelik, katalog ve ayırtma kodlarına dokunulur: dört ayrı sahiple takvim uzlaştırılır. Merkezî düzende yalnız iki birim yazılır — ayırtmanın giriş ve çıkış uyarlayıcısı — ve dokunulan sahip sayısı ikiye iner. Yeni sistemin maliyeti sistem sayısından kopar.

Son satır bunun karşılığını gösteriyor. Merkezde dört kural duruyor ve dördünün de asıl sahibi başkasıdır: gecikme ücreti ücretlendirmenin, rapor dönemi yönetim biriminin, bölge tanımı şube biriminin, üye sınıfı üyelik biriminin işidir. Merkezin sahibi olan tümleştirme birimi bu kuralların hiçbirini yazacak yetkinliğe sahip değildir ama hepsinin kodunu taşır. Bunun iki sonucu var. Birincisi, üye sınıfı tanımı değiştiğinde değişiklik üyelik biriminin kendi sürümüyle değil, merkezin sürümüyle çıkar; ikincisi, o sürüm dokuz akışın tamamını birden yeniden dağıtır. Kuralın tekrarı kalkarken kuralın sahipliği bulanıklaşıyor ve dağıtım birimleri birbirine bağlanıyor. Seçim akış grafının ne kadar sıkı olduğuna bağlıdır: merkez tekrarı ve yeni sistemin koordinasyon maliyetini düşürür, karşılığında bir atlama, ortak bir arıza noktası ve sahibi belirsiz bir kural yığını ekler.

Özet

  • Aynı dokuz akış iki düzende kuruldu ve ikisi de aynı 18 kaydı birebir aynı içerikle teslim etti; karşılaştırma bu doğrulamanın üstüne kuruldu.
  • Yazılan birim sayısı 9’a karşı 8; altı sistemin 30 olası yönlü çiftinden yalnız 9’u gerçekleştiği için merkezin bağ sayısı kazancı tek birimde kaldı.
  • Aynı dört kural ikili düzende 8, merkezde 5 yerde geçiyor; rapor dönemi kuralı üç ayrı sistemde tekrarlanıyor.
  • Merkez altı olay için 24 ileti işliyor, en yoğun ikili bağ 2; merkez kapatıldığında 18 teslimden 0’ı gerçekleşirken ikili düzen aynı anda 18’ini tamamlıyor.
  • Yedinci sistem ikili düzende 5 birim ve 4 sahibe, merkezî düzende 2 birim ve 2 sahibe mal oluyor; karşılığında merkezde sahibi başkası olan 4 kural birikiyor.

Sonraki Adım

İki ders boyunca veri hep tek yönde aktı: bir sistem yazdı, ötekiler okudu. Kaynak belliydi, bağın yönü belliydi ve çakışma diye bir şey yoktu. Kurumda bu varsayım çoğu varlık için tutmaz. Üye kaydı üyelik sisteminde de, ödünç servisinde de, belediyedeki kimlik servisinde de durur; üçü de kendi kullanıcı arayüzünden yazılır ve üçünün adres alanı aynı üye için farklı değerler taşır. Sonraki ders aynı varlığı üç sistemde tutar ve sayar: kaç sistem yazıyor, kaç alan çakışıyor, bir uzlaştırma turunda kaç kayıt düzeliyor ve geriye kaç tutarsızlık kalıyor; bir sistem kaynak seçildiğinde bu sayılar nasıl değişiyor ve o seçim yazma yolunda neyi uzatıyor.

İ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