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.