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.
olcumsunucusu yereldir ve bir süreçtir.tarifeilekayituzaktı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 kapaliile 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.