Ders 01 / 18
Tek Parça Uygulama
Çalışan bir tek parça ödünç sisteminin kurulması ve ölçülmesi: yedi modül, on ithal kenarı, tek süreç, sıfır ağ atlaması; yerel çağrının ucuzluğu, tek dağıtım biriminin getirdiği değiştirme kolaylığı ve aynı birimin çoğaltılmasıyla ortaya çıkan ölçek sınırı.
İçindekiler
Önceki kurs, Eşzamansız İşleme, kapanışında tek bir varsayımı işaret etti: bütün kuyruklar, teslim garantileri ve geri basınç düzenekleri tek bir dağıtım biriminin içinde kuruldu. Bu kurs o birimi bölmeyi ele alır. Bölmeden önce bölünecek şeyin ne olduğu ortada durmalıdır.
Bu ders, kütüphane ödünç sisteminin çalışan bir tek parça (monolith) gerçekleştirmesini kurar ve ölçer. Kurulan düzenek sonraki beş dersin tabanıdır: her ders onu bir adım böler ya da bölmenin bedelini bu ölçülerin üzerinden sayar. Mimari biçemlerin tanımı Mimari Biçemler kursunda yapıldı; buradaki iş tanım değil, gerçekleştirme ve ölçüdür.
Düzeneğin Sınırı
MS1. Durum tek bir JSON dosyasında tutulur. Gerçek bir dağıtımda burada paylaşılan bir veri deposu olurdu; modelin taşıdığı özellik, durumun süreç dışında ve tek yerde olmasıdır.
MS2. Modüller aynı süreçte, yerel işlev çağrısıyla konuşur. Ağ gecikmesi, serileştirme ve kısmi hata bu modelde yoktur; onlar dördüncü derste gerçek süreçlerle ölçülür.
Düzenek: Tek Parça Ödünç Sistemi
Sistem beş iş alanı taşır: katalog, üyelik, ödünç, bildirim ve ücretlendirme. Durumu tutan ortak bir modül var.
// depo.mjs — butun modullerin durumu; tek dosya, tek surec import { existsSync, readFileSync, writeFileSync } from "node:fs"; const YOL = "veri.json"; const BASLANGIC = { kitap: { 1: { baslik: "Korluk", durum: "rafta" }, 2: { baslik: "Kum Kitabi", durum: "rafta" } }, uye: { 4: { ad: "Emre", acik: 1, sinir: 3, borc: 0 }, 5: { ad: "Selin", acik: 3, sinir: 3, borc: 0 } }, odunc: [], }; export function oku() { if (existsSync(YOL) === false) writeFileSync(YOL, JSON.stringify(BASLANGIC)); return JSON.parse(readFileSync(YOL, "utf8")); } export function yaz(d) { writeFileSync(YOL, JSON.stringify(d)); }
Üç iş modülü bu depoyu okur ve yazar; bildirim modülünün durumu yoktur.
// katalog.mjs — kitap kayitlari import { oku, yaz } from "./depo.mjs"; export function durum(kitapId) { return oku().kitap[kitapId].durum; } export function oduncIsaretle(kitapId) { const d = oku(); if (d.kitap[kitapId].durum !== "rafta") throw new Error("kitap rafta degil"); d.kitap[kitapId].durum = "odunc"; yaz(d); }
// uyelik.mjs — uye kayitlari ve odunc siniri import { oku, yaz } from "./depo.mjs"; export function uygunMu(uyeId) { const u = oku().uye[uyeId]; return u.acik < u.sinir; } export function oduncEkle(uyeId) { const d = oku(); d.uye[uyeId].acik += 1; yaz(d); }
// ucretlendirme.mjs — gecikme ucreti import { oku, yaz } from "./depo.mjs"; export const GUNLUK = 2; export function ucret(gecikmeGun) { return gecikmeGun > 0 ? gecikmeGun * GUNLUK : 0; } export function borcEkle(uyeId, tutar) { const d = oku(); d.uye[uyeId].borc += tutar; yaz(d); }
// bildirim.mjs — uyeye bildirim export function gonder(uyeId, metin) { console.log(` bildirim -> uye ${uyeId}: ${metin}`); }
İş akışını yürüten modül dördünü de sırayla çağırır.
// odunc.mjs — odunc verme is akisi; dort modulu sirayla cagirir import { oku, yaz } from "./depo.mjs"; import * as katalog from "./katalog.mjs"; import * as uyelik from "./uyelik.mjs"; import * as ucretlendirme from "./ucretlendirme.mjs"; import * as bildirim from "./bildirim.mjs"; export function oduncVer(kitapId, uyeId, gecikmeGun = 0) { if (uyelik.uygunMu(uyeId) === false) throw new Error("odunc siniri asildi"); katalog.oduncIsaretle(kitapId); uyelik.oduncEkle(uyeId); const tutar = ucretlendirme.ucret(gecikmeGun); if (tutar > 0) ucretlendirme.borcEkle(uyeId, tutar); const d = oku(); d.odunc.push({ kitapId, uyeId }); yaz(d); bildirim.gonder(uyeId, `kitap ${kitapId} odunc verildi`); return { kitapId, uyeId, tutar }; }
// uygulama.mjs — tek giris noktasi; is akisini bastan sona kosturur import { oduncVer } from "./odunc.mjs"; import { durum } from "./katalog.mjs"; const [kitapId = 1, uyeId = 4, gecikme = 0] = process.argv.slice(2).map(Number); try { const s = oduncVer(kitapId, uyeId, gecikme); console.log(`odunc verildi: kitap ${s.kitapId} -> uye ${s.uyeId}, ucret ${s.tutar}`); } catch (h) { console.log("basarisiz:", h.message); } console.log(` katalog: kitap ${kitapId} = ${durum(kitapId)}`);
İş Akışının Koşumu
Dört istek dört yolu sınar: geçen bir ödünç, ödünçteki bir kitabın ikinci kez istenmesi, sınırındaki bir üye ve gecikme ücreti çıkaran bir kayıt.
rm -f veri.json node uygulama.mjs 1 4 echo "--- ayni kitap ikinci kez ---" node uygulama.mjs 1 4 echo "--- sinirdaki uye ---" node uygulama.mjs 2 5 echo "--- gecikmeli kayit ---" node uygulama.mjs 2 4 3
bildirim -> uye 4: kitap 1 odunc verildi odunc verildi: kitap 1 -> uye 4, ucret 0 katalog: kitap 1 = odunc --- ayni kitap ikinci kez --- basarisiz: kitap rafta degil katalog: kitap 1 = odunc --- sinirdaki uye --- basarisiz: odunc siniri asildi katalog: kitap 2 = rafta --- gecikmeli kayit --- bildirim -> uye 4: kitap 2 odunc verildi odunc verildi: kitap 2 -> uye 4, ucret 6 katalog: kitap 2 = odunc
Üçüncü koşumda dikkat çeken şey, üyelik denetiminin katalogdan önce yapılmış olmasıdır:
sınır aşıldığı için kitap hiç işaretlenmedi. Bu sıralamayı ayakta tutan şey, iki denetimin de
aynı süreçte, aynı oduncVer gövdesinde durmasıdır. Beşinci koşumdan sonra bu sıralama bir
sözleşmeye dönüşecek; şimdilik bir if satırıdır.
Ölçü
Mimari tartışmasının taşıyacağı sayılar kaynak dosyalardan okunur.
// olc.mjs — modul, ithal kenari ve is akisindaki modullerarasi cagri sayisi import { readdirSync, readFileSync } from "node:fs"; const KENAR = /from "\.\/([\w-]+\.mjs)"/g; const dosyalar = readdirSync(".").filter((a) => a.endsWith(".mjs") && a !== "olc.mjs").sort(); let toplam = 0; for (const d of dosyalar) { const hedef = [...readFileSync(d, "utf8").matchAll(KENAR)].map((m) => m[1].replace(".mjs", "")); toplam += hedef.length; console.log(`${d.padEnd(18)} -> ${hedef.join(", ") || "(yok)"}`); } const akis = [...readFileSync("odunc.mjs", "utf8") .matchAll(/\b(katalog|uyelik|ucretlendirme|bildirim)\.\w+\(/g)]; console.log(`modul: ${dosyalar.length} ithal kenari: ${toplam}`); console.log(`is akisindaki modullerarasi cagri: ${akis.length} ag atlamasi: 0`); console.log(`surec: 1 dagitim birimi: 1 baslatma adimi: 1`);
node olc.mjs
bildirim.mjs -> (yok) depo.mjs -> (yok) katalog.mjs -> depo odunc.mjs -> depo, katalog, uyelik, ucretlendirme, bildirim ucretlendirme.mjs -> depo uyelik.mjs -> depo uygulama.mjs -> odunc, katalog modul: 7 ithal kenari: 10 is akisindaki modullerarasi cagri: 6 ag atlamasi: 0 surec: 1 dagitim birimi: 1 baslatma adimi: 1
Yedi modül, on ithal kenarı, altı modüller arası çağrı. Bu altı çağrının hepsi yerel: yığıt
üzerinde parametre geçirir, dönüş değerini doğrudan alır, hata throw ile yayılır. Ağ
atlaması sıfırdır. Sistemi ayağa kaldırmak tek adımdır; ödünç iş akışını uçtan uca koşturmak
için başka bir sürecin hazır olmasını beklemek gerekmez.
Bu üç sayı — altı yerel çağrı, sıfır atlama, bir dağıtım birimi — tek parçanın ucuzlattığı
şeyi tek başına anlatır. Bir çağrının yarı yolda kaybolması diye bir durum yoktur; ya işlev
döner ya hata atar. Katalog ile üyelik arasındaki tutarlılık için bir düzen kurmak gerekmez,
çünkü ikisi de aynı veri.json dosyasına aynı süreçten yazar. Beş alana yayılan bir
değişiklik tek bir derlemede sınanır ve tek bir dağıtımda yayına çıkar.
Ölçek Sınırı
Bedel, aynı birimin bölünememesinde görünür. Ödünç yolunun yükü arttığında yapılabilecek tek şey, birimin tamamını çoğaltmaktır.
# olcek.sh — ayni tek parcanin uc kopyasi; her kopya butun modulleri tasir rm -rf kopya1 kopya2 kopya3 for k in kopya1 kopya2 kopya3; do mkdir "$k" for m in depo katalog uyelik ucretlendirme bildirim odunc uygulama; do cp "$m.mjs" "$k/"; done ( cd "$k" && node uygulama.mjs 1 4 | grep '^odunc' | sed "s|^|$k: |" ) done echo "dagitilan modul dosyasi: $(ls kopya*/*.mjs | wc -l | tr -d ' ')"
kopya1: odunc verildi: kitap 1 -> uye 4, ucret 0 kopya2: odunc verildi: kitap 1 -> uye 4, ucret 0 kopya3: odunc verildi: kitap 1 -> uye 4, ucret 0 dagitilan modul dosyasi: 21
İki sayı okunmalı. Birincisi: yalnız ödünç yoluna kapasite eklemek istenmesine karşın dağıtılan modül dosyası 7’den 21’e çıktı. Bildirim ve ücretlendirme kodunun üç kopyası, ihtiyaç duyulmadığı hâlde ayakta duruyor. Ölçekleme birimi kod tabanının tamamıdır; darboğazın hangi modülde olduğu bu birimi küçültmez.
İkincisi, koşumun kendisi: üç kopya da aynı kitabı ödünç verdi. Her kopya kendi
veri.json dosyasını taşıdığı için üçü de kitabı rafta gördü. Gerçek bir dağıtımda durum
ortak bir depoya taşınır ve bu üçleme ortadan kalkar — ama o zaman da bütün kopyalar tek bir
depoya yüklenir. Tek parçanın ölçek sınırı buradadır: süreç çoğaltılabilir, durum çoğaltılamaz.
Değiştirme tarafında da bir sınır var. Dağıtım birimi bir tane olduğu için, ücret oranındaki tek satırlık bir değişiklik de katalog ile üyeliği yeniden yayına çıkarır. Ölçüde bunun karşılığı şudur: dokunulan dosya bir, yeniden dağıtılan modül yedi.
Üç Sütun
Bu kursun her dersi aynı işi iki gerçekleştirmede karşılaştırır ve üç sütunu doldurur.
| Tek parça | |
|---|---|
| Ucuzlattığı | Yerel çağrı, ortak işlem sınırı, tek derleme, tek dağıtım adımı |
| Pahalılaştırdığı | Ölçekleme birimi tüm kod tabanı; bir satırlık değişiklik yedi modülü yeniden yayınlar |
| Doğan arıza kipi | Bir modüldeki bellek sızıntısı ya da sonsuz döngü bütün alanları birlikte durdurur |
Üçüncü satır ölçülmedi, çünkü tek süreçte bunu ölçmenin yolu süreci çökertmektir. Kayıt olarak bırakılıyor: dördüncü derste aynı arıza ayrı süreçlerde denendiğinde karşılaştırılacak.
Özet
- Kütüphane ödünç sistemi tek parça olarak kuruldu: 7 modül, 10 ithal kenarı, 1 süreç, 1 dağıtım birimi; ödünç iş akışı uçtan uca koşuyor.
- İş akışındaki 6 modüller arası çağrının hepsi yerel; ağ atlaması sıfır, başlatma adımı bir.
- Tek işlem sınırı ve yerel çağrı, katalog ile üyelik arasındaki tutarlılığı ek bir düzen gerektirmeden sağlıyor.
- Ölçek sınırı çoğaltmada görünür: yalnız ödünç yoluna kapasite eklemek 7 modülü 21 dosyaya çıkardı, çünkü ölçekleme birimi kod tabanının tamamıdır.
- Tek dağıtım birimi, tek satırlık bir ücret değişikliğinde bile bütün modülleri yeniden yayına çıkarır.
Sonraki Adım
Ölçülen sayılardan biri henüz sorgulanmadı: odunc.mjs dört modülü de doğrudan ithal
ediyor, ama bunu engelleyen bir şey yok — bildirim.mjs de katalog.mjs dosyasını ithal
edebilir, katalog da üyeliğin içine uzanabilir. Tek parçanın kod içinde bir sınırı yoktur;
ithal grafiği kimse bakmazsa tamamlanmış bir grafiğe doğru büyür. Sonraki ders bu grafiği
ölçer, bir modülün başkasının içine uzandığı sınır ihlallerini sayar ve sınırın kod
içinde bir kuralla korunduğu düzeni kurar: süreç hâlâ tek, dağıtım birimi hâlâ tek, ama
ithal kenarları artık serbest değil.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.