İçeriğe geç
academia.sh

Ders 08 / 11

Yerel Sunucuya Bağlanma

Yerel bir sunucunun ayrı bir süreç olarak başlatılması ve bağlantının bedelinin ilk çağrıya kadar geçen mesajla sayılması: süreci başlatmak hazır bildirimiyle 1 mesaj ve 10 belirteç, sunucu keşfi 6 mesaj ve 99 belirteç tutar, yani ilk araç çağrısı gönderilmeden önce 7 mesaj ve 109 belirteç harcanır. Aynı kurulumun ardından koşan üç araçlık zincir 3 istek, 3 yanıt, 80 belirteç ve 4 gidiş-dönüşle A017 abonesi için 844 verir; kurulum tek görevlik bir oturumda işten pahalıdır. On iki görevde kurulum 7 mesajda kalırken iş 60 mesaja çıkar. Süreç düştüğünde ne hata ne mesaj üretilir, kayıp yalnız sonraki çağrının gönderilememesiyle anlaşılır ve toparlanma kurulumun tamamını yeniden ödetir: bir düşme belirteci 919'dan 1028'e, üç düşme 1246'ya çıkarır, tamamlanan görev on ikide kalır.

İçindekiler

Önceki iki derste sunucu ve istemci yazıldı: yetenek bildiriminin eksiksizliği çağrılabilirlik olarak sayıldı, keşfin bedeli çıkarıldı ve sürüm sapmasıyla düşen aracın hiçbir hata üretmediği görüldü. İkisi de tek bir varsayımın üstünde duruyordu. Sunucu oradaydı: yetenek sorulduğunda yanıt veriyordu, çağrı gönderildiğinde karşılıyordu.

Yerel bir sunucu için bu varsayım kurulmaz, kurulur. Yerel sunucu aynı makinede çalışan ayrı bir süreçtir: birinin onu başlatması gerekir, hazır olduğunu söylemesi gerekir ve öldüğünde oturumun tuttuğu her şey onunla birlikte gider. Bu dersin sorusu şudur: ilk işe yarar çağrı gönderilene kadar kaç mesaj geçer, o zincir ne tutar, ve süreç düştüğünde kim yeniden öder.

  • GE20. Ortam, üç sunucu, süreç ve protokol benzeticisi kurgudur; gerçek bir süreç başlatılmaz, gerçek bir sunucu ya da çalışma zamanı çağrılmaz. Tohum 20260218, ikinci koşum 20260219.
  • GE21. olcum sunucusu yereldir ve bir süreçtir. tarife ile kayit uzaktır; bağlantı biçimleri sonraki derse bırakılır, burada yalnız zincirin içinde yer alırlar.
  • GE22. Süreç başlatıldığında sunucu hazır olduğunu bir bildirim mesajıyla söyler. Bildirim yanıt beklemez: zarf bedelini öder ama gidiş-dönüş eklemez.
  • GE23. Sunucu keşfi süreç başlatıldıktan sonra yapılır. Kurulum, süreç başlatma ile keşfin toplamıdır.
  • GE24. Süreci kapalı bir yerel sunucuya yapılan çağrı hiç gönderilemez; istemcinin kendi içinde surec kapali ile biter ve mesaj harcamaz.
  • GE25. Süreç sessizce ölür: ölüm ne bir hata ne bir mesaj üretir. İstemci bunu ancak sonraki çağrıyı gönderemediğinde anlar.
  • GE26. Toparlanma şudur: süreç yeniden başlatılır, oturum baştan kurulur ve başarısız çağrı bir kez yinelenir. Kurulumun tamamı yeniden ödenir.
  • GE27. Görev kümesi 12 kurgu görevdir; iki türden altışar tane (bedel ve tüketim). Bu kümede bütün araçlar anlaşılan kümededir, böylece değişen tek şey süreçtir.
  • GE28. Zarfın sabit bedeli 8 belirteçtir; uzak aktarımda gidiş-dönüş iki katıdır.
  • GE29. Taban çizgisi protokolsüz kurulumdur: aracın ders içinde tanımlandığı, başlatılacak bir süreç ve sorulacak bir yetenek olmadığı hâl. Orada kurulum bedeli sıfırdır.

Süreç, Hazır Bildirimi ve İlk Çağrı

Aşağıdaki kod kurgu ortamı, üç kurgu sunucuyu ve yetenek uzlaşmasını kurar, sonra yerel sunucuyu bir sürece bağlar: baslat süreci açar ve hazır bildirimini gönderir, cagir_yerel süreci kapalı bir sunucuya çağrı gönderilmesini engeller, kurulum ikisini birleştirir.

TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


def belirtecle(m):
    b = []
    for s in str(m).lower().replace(",", " , ").split():
        while len(s) > 5:
            b.append(s[:5])
            s = s[5:]
        b.append(s)
    return b


# ---- KURGU ortam ve araclar (K05 ile ayni kurgu, burada sunucuya tasiniyor)
BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
ABONE = {}
for i in range(60):
    r = uretec(TOHUM + 31 * i)
    onceki = 10000 + int(r() * 80000)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "onceki": onceki, "simdiki": onceki + int(r() * 70)}
BASAMAK, USTU, ISYERI_KAT = ((10, 4), (25, 7), (40, 11)), 16, 2


def _bedel(m3, tip):
    kalan, alt, top = m3, 0, 0
    for ust, birim in BASAMAK:
        dilim = min(kalan, ust - alt)
        if dilim <= 0:
            break
        top += dilim * birim
        kalan -= dilim
        alt = ust
    return (top + kalan * USTU) * (ISYERI_KAT if tip == "isyeri" else 1)


def t_sayac(abone=None, **_):
    a = ABONE.get(abone)
    return {"hata": "abone yok"} if a is None else {
        "onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"]}


def t_tuketim(onceki=None, simdiki=None, **_):
    if onceki is None or simdiki is None:
        return {"hata": "eksik alan"}
    f = simdiki - onceki
    return {"m3": f + 100000 if f < 0 else f}


def t_tarife(m3=None, tip="mesken", **_):
    return {"hata": "eksik alan"} if m3 is None else {"tutar": _bedel(m3, tip)}


def t_bolge(bolge=None, **_):
    if bolge not in BOLGE:
        return {"hata": "bolge yok"}
    n = sum(1 for a in ABONE.values() if a["bolge"] == bolge)
    return {"bolge": bolge, "abone": n}


def t_kayit(abone=None, sutun=None, deger=None, **_):
    if not (abone and sutun and deger is not None):
        return {"hata": "eksik alan"}
    return {"yazildi": True}


# ---- KURGU sunucular: olcum yerel bir surec , otekiler uzak
SUNUCU = {
    "olcum": {"surum": 2, "aktarim": "yerel",
              "arac": {"sayac_oku": {"islev": t_sayac, "alan": ("abone",), "yan_etki": False},
                       "tuketim": {"islev": t_tuketim, "alan": ("onceki", "simdiki"),
                                   "yan_etki": False}},
              "kaynak": ("abone_listesi",), "istem": ()},
    "tarife": {"surum": 1, "aktarim": "uzak",
               "arac": {"tarife": {"islev": t_tarife, "alan": ("m3", "tip"), "yan_etki": False}},
               "kaynak": ("basamak_tablosu",), "istem": ("bedel_aciklamasi",)},
    "kayit": {"surum": 3, "aktarim": "uzak",
              "arac": {"bolge_ozet": {"islev": t_bolge, "alan": ("bolge",), "yan_etki": False},
                       "kayit_yaz": {"islev": t_kayit, "alan": ("abone", "sutun", "deger"),
                                     "yan_etki": True}},
              "kaynak": (), "istem": ()},
}
ISTEMCI_SURUM = {"olcum": 2, "tarife": 1, "kayit": 2}      # kayit sunucusu ileride
IZIN = ("sayac_oku", "tuketim", "tarife", "bolge_ozet")     # kayit_yaz izin listesinde yok

SAYIM = {"istek": 0, "yanit": 0, "bildirim": 0, "belirtec": 0, "gidis_donus": 0}


def mesaj(tur, govde, aktarim="yerel"):
    """KURGU zarf. Gercek bir protokol uygulanmaz; sayilan sey zarfin bedelidir."""
    SAYIM[tur] += 1
    b = len(belirtecle(govde)) + 8                       # zarf sabiti
    SAYIM["belirtec"] += b
    if tur == "istek":
        SAYIM["gidis_donus"] += 2 if aktarim == "uzak" else 1
    return b


def elsikismasi(istemci_surum=None, izin=None):
    """Yetenek uzlasmasi: surum uyusmazligi ve izin listesi arac kumesini daraltir."""
    istemci_surum = ISTEMCI_SURUM if istemci_surum is None else istemci_surum
    izin = IZIN if izin is None else izin
    anlasilan, dusen = {}, []
    for ad, s in SUNUCU.items():
        mesaj("istek", f"yetenek sor {ad}", s["aktarim"])
        mesaj("yanit", f"yetenek bildir {ad} surum {s['surum']} "
                       + " ".join(s["arac"]) + " " + " ".join(s["kaynak"]))
        if istemci_surum.get(ad, 0) < s["surum"]:
            dusen.append((ad, "surum", s["surum"], istemci_surum.get(ad, 0)))
            continue
        for t, tan in s["arac"].items():
            if t not in izin:
                dusen.append((ad, "izin", t, None))
                continue
            anlasilan[t] = dict(tan, sunucu=ad, aktarim=s["aktarim"])
    return anlasilan, dusen


def cagir(anlasilan, ad, durum):
    if ad not in anlasilan:
        return {"hata": "arac bildirilmedi"}
    t = anlasilan[ad]
    kw = {a: durum[a] for a in t["alan"] if a in durum}
    mesaj("istek", f"{ad} " + " ".join(f"{k} {v}" for k, v in kw.items()), t["aktarim"])
    s = t["islev"](**kw)
    mesaj("yanit", " ".join(f"{k} {v}" for k, v in s.items()))
    return s


# ---- Yerel sunucu bir surectir: baslatilir , hazir oldugunu bildirir , olur
SUREC = {ad: False for ad, s in SUNUCU.items() if s["aktarim"] == "yerel"}


def baslat(ad):
    """Sureci baslatir; sunucu hazir oldugunu yanit beklemeyen bir bildirimle soyler."""
    SUREC[ad] = True
    mesaj("bildirim", f"{ad} hazir")


def cagir_yerel(anlasilan, ad, durum):
    """Sureci kapali bir yerel sunucuya cagri hic gonderilemez."""
    t = anlasilan.get(ad)
    if t is not None and SUREC.get(t["sunucu"]) is False:
        return {"hata": "surec kapali"}
    return cagir(anlasilan, ad, durum)


def kurulum(istemci_surum=None):
    """Yerel surec + sunucu kesfi: ilk cagriya kadar gecen her sey."""
    for k in SAYIM:
        SAYIM[k] = 0
    for ad in SUREC:
        baslat(ad)
    anlasilan = elsikismasi(istemci_surum)[0]
    return anlasilan, dict(SAYIM)


anlasilan, k = kurulum()
print(f"ilk cagriya kadar : bildirim {k['bildirim']} , istek {k['istek']} ,"
      f" yanit {k['yanit']} , toplam mesaj {k['bildirim'] + k['istek'] + k['yanit']} ,"
      f" belirtec {k['belirtec']} , gidis-donus {k['gidis_donus']}")
for key in SAYIM:
    SAYIM[key] = 0
durum = {"abone": "A017"}
for ad in ("sayac_oku", "tuketim", "tarife"):
    durum.update(cagir_yerel(anlasilan, ad, durum))
print(f"ilk zincir ( A017 ) : istek {SAYIM['istek']} , yanit {SAYIM['yanit']} ,"
      f" belirtec {SAYIM['belirtec']} , gidis-donus {SAYIM['gidis_donus']} ,"
      f" sonuc {durum['tutar']}")
ilk cagriya kadar : bildirim 1 , istek 3 , yanit 3 , toplam mesaj 7 , belirtec 109 , gidis-donus 5
ilk zincir ( A017 ) : istek 3 , yanit 3 , belirtec 80 , gidis-donus 4 , sonuc 844

İlk satır bu dersin ana ölçüsüdür. Bir araç çağrısı gönderilmeden önce 7 mesaj ve 109 belirteç harcanmıştır: süreç başlatma bildirimi bir mesaj ve 10 belirteç, sunucu keşfi altı mesaj ve 99 belirteç. Hazır bildirimi zarf bedelini ödüyor ama gidiş-dönüşe hiç dokunmuyor, çünkü yanıt beklemiyor; beşin tamamı keşfin gidiş-dönüşüdür.

İkinci satır aynı kurulumun üstünde koşan zincirdir ve karşılaştırma buradan çıkar. Üç araçlık zincir 3 istek, 3 yanıt, 80 belirteç, 4 gidiş-dönüş tutuyor ve A017 abonesi için 844 veriyor. Üç istek için dört gidiş-dönüş sayılmasının nedeni zincirin ikiye bölünmüş olmasıdır: sayac_oku ile tuketim yerel süreçle konuşur ve birer gidiş-dönüş sayar, tarife uzaktadır ve iki sayar. Tek görevlik bir oturumda kurulum işten pahalıdır: 7 mesaja karşı 6, 109 belirtece karşı 80. Protokolsüz kurulumda bu 109 belirtecin karşılığı sıfırdır, çünkü başlatılacak süreç de sorulacak yetenek de yoktur.

Süreç Düşerse Kim Öder

Aşağıdaki kod on iki kurgu görevi aynı süreç üzerinde koşturur. Görevlerin hepsi anlaşılan araç kümesiyle çözülebilir, böylece değişen tek şey sürecin ayakta kalıp kalmadığı olur. Süreç belirlenmiş görev sıralarında sessizce ölür.

ZINCIR = {"bedel": ("sayac_oku", "tuketim", "tarife"), "tuketim": ("sayac_oku", "tuketim")}


def gorevler(n=12, tohum=TOHUM):
    """KURGU gorevler: iki turden esit sayida, abonesi tohumla secilir."""
    g = []
    for i in range(n):
        r = uretec(tohum + 9973 * i)
        g.append({"tur": list(ZINCIR)[i % 2], "abone": f"A{1 + int(r() * 60):03d}"})
    return g


def dogru(g):
    a = ABONE[g["abone"]]
    f = a["simdiki"] - a["onceki"]
    m3 = f + 100000 if f < 0 else f
    return m3 if g["tur"] == "tuketim" else _bedel(m3, a["tip"])


def toparla(oturum):
    """Dusen surec yeniden baslatilir ve oturum bastan kurulur: kesif yeniden odenir."""
    for ad, acik in SUREC.items():
        if not acik:
            baslat(ad)
    oturum["anlasilan"] = elsikismasi()[0]
    oturum["yeniden"] += 1


def yurut(oturum, g):
    durum, sonuc = {"abone": g["abone"]}, None
    for ad in ZINCIR[g["tur"]]:
        y = cagir_yerel(oturum["anlasilan"], ad, durum)
        if y.get("hata") == "surec kapali":
            toparla(oturum)
            y = cagir_yerel(oturum["anlasilan"], ad, durum)
        if "hata" in y:
            break
        durum.update(y)
        sonuc = y.get("tutar", y.get("m3", sonuc))
    return sonuc == dogru(g)


def kosum(n=12, dusme=(), tohum=TOHUM):
    """Ayni gorevler; degisken, surecin kac kez dustugu."""
    for ad in SUREC:
        SUREC[ad] = False
    anlasilan, k = kurulum()
    oturum = {"anlasilan": anlasilan, "yeniden": 0}
    tamam = 0
    for i, g in enumerate(gorevler(n, tohum)):
        if i in dusme:
            SUREC["olcum"] = False               # surec sessizce oluyor , mesaj yok
        tamam += yurut(oturum, g)
    return dict(yeniden=oturum["yeniden"], tamam=tamam, **SAYIM)


print("kurulum                     yeniden bildirim istek yanit belirtec gidis tamamlanan")
for ad, n, dus in (("1 gorev , dusme yok", 1, ()), ("12 gorev , dusme yok", 12, ()),
                   ("12 gorev , 1 surec dusmesi", 12, (4,)),
                   ("12 gorev , 3 surec dusmesi", 12, (2, 5, 9))):
    a = kosum(n, dus)
    print(f"{ad:<28} {a['yeniden']:>5} {a['bildirim']:>8} {a['istek']:>6} {a['yanit']:>5}"
          f" {a['belirtec']:>8} {a['gidis_donus']:>5} {a['tamam']:>10}")
b = kosum(12, (4,), TOHUM + 1)
print("ikinci kosum ( gorev tohumu 20260219 , 1 dusme ) :", dict(
    tamamlanan=b["tamam"], mesaj=b["bildirim"] + b["istek"] + b["yanit"], belirtec=b["belirtec"]))
kurulum                     yeniden bildirim istek yanit belirtec gidis tamamlanan
1 gorev , dusme yok              0        1      6     6      189     9          1
12 gorev , dusme yok             0        1     33    33      919    41         12
12 gorev , 1 surec dusmesi       1        2     36    36     1028    46         12
12 gorev , 3 surec dusmesi       3        4     42    42     1246    56         12
ikinci kosum ( gorev tohumu 20260219 , 1 dusme ) : {'tamamlanan': 12, 'mesaj': 74, 'belirtec': 1028}

İlk iki satır kurulumun nasıl seyreldiğini gösteriyor. Tek görevlik oturumda toplam 13 mesajın yedisi kurulumdur; on iki göreve çıkıldığında kurulum yine 7 mesaj ve 109 belirteçte kalır, iş ise 60 mesaja ve 810 belirtece çıkar. Kurulum sabit bir bedeldir ve oturum uzadıkça payı düşer; bir oturumu kısa tutmanın bedeli tam olarak budur.

Üçüncü ve dördüncü satırlar sürecin ömrünü sayıya çevirir. Süreç öldüğünde ne bir hata mesajı ne bir bildirim üretilir; ölüm mesaj akışında görünmez. İstemci bunu ancak bir sonraki çağrıyı gönderemediğinde anlar ve o çağrı da hiç tele çıkmadığı için sayıma girmez. Toparlanma ise kurulumun tamamını yeniden ödetir: bir düşme mesajı 67’den 74’e, zarf belirtecini 919’dan 1028’e, gidiş-dönüşü 41’den 46’ya çıkarır — yani tam olarak bir kurulum kadar. Üç düşmede belirteç 1246 olur; eklenen 327 belirteç, dört bedel zincirinin bedeline yakındır.

Tamamlanan görev dört satırda da düşmedi. Bu, toparlanmanın işi kurtardığı anlamına gelir ve bedelin nereye yazılacağını da söyler: süreç düşmesi bu kurguda bir doğruluk sorunu değil, bir mesaj sorunudur. İkinci koşumda görevler başka bir tohumla çekilir ve üç sayı da aynı kalır; ölçü verinin değil sözleşmenin ve sürecin özelliğidir.

Özet

  • Yerel sunucu ayrı bir süreçtir: başlatılır, hazır olduğunu yanıt beklemeyen bir bildirim mesajıyla söyler ve oturumun ömrü onun ömrüyle sınırlıdır.
  • İlk araç çağrısına kadar 7 mesaj ve 109 belirteç geçer: süreç başlatma 1 mesaj ve 10 belirteç, sunucu keşfi 6 mesaj ve 99 belirteç. Bildirim gidiş-dönüş eklemez.
  • Üç araçlık zincir 3 istek, 3 yanıt, 80 belirteç ve 4 gidiş-dönüş tutar; iki yerel adım birer, bir uzak adım iki gidiş-dönüş sayar.
  • Tek görevlik oturumda kurulum işten pahalıdır (7 mesaja karşı 6); on iki görevde kurulum yine 7 mesajda kalırken iş 60 mesaja çıkar.
  • Süreç ölümü ne hata ne mesaj üretir; yalnız sonraki çağrının gönderilememesiyle anlaşılır.
  • Toparlanma kurulumun tamamını yeniden ödetir: bir düşme belirteci 919’dan 1028’e, üç düşme 1246’ya çıkarır, tamamlanan görev on ikide kalır.

Sonraki Adım

Bu üç derste sunucu yazıldı, istemci yazıldı ve ikisi arasındaki bağlantı bir süreç olarak kuruldu. Bütün ölçümlerin ortak bir zemini vardı: sunucu aynı makinedeydi. Süreci başlatan, öldüğünü fark eden ve yeniden kuran taraf ile sunucunun kendisi tek bir sınırın içinde duruyordu; kimin kim olduğu hiç sorulmadı, çünkü sorulacak bir şey yoktu. Sunucu ağın ötesine geçtiğinde bağlantının kendisi bir güven sorusu hâline gelir. Sonraki ders uzak bağlantıyı kurar ve yalnız onun eklediği adımları sayar: kimlik adımı, yetki adımı ve gidiş-dönüş, yerel kurulumun sayılarıyla yan yana.

İ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