Ders 04 / 11
Veri Katmanı
İstek, yanıt ve bildirim mesajlarının bedelinin sayılması: sekiz abonelik bir toplu iş protokolsüz kurulumda 24 işlev çağrısı, sıfır mesaj ve sıfır zarf belirteciyle biterken protokollü kurulumda 48 mesaja, 640 belirtece ve 32 gidiş-dönüşe çıkar ve 640 belirtecin 384'ü yalnız zarftır. Aynı ilerleme bilgisi istek-yanıt çiftiyle taşındığında toplam 1147 belirteç ile 80 gidiş-dönüş, bildirim olarak taşındığında 976 belirteç ile 32 gidiş-dönüş eder; bildirim 19 mesaj ve 48 gidiş-dönüş kazandırır. Bedeli, hatta düşen beş bildirimin gönderen tarafta hiçbir iz bırakmamasıdır: aynı kayıp istek-yanıt tasarımında beş eksik yanıt olarak görünür, bildirimde görülen kayıp iki koşumda da sıfırdır.
İçindekiler
Önceki üç derste bağlantının nasıl kurulduğu ölçüldü: araç bağlantısının bir belirtime bağlanması, barındırıcı, istemci ve sunucu arasındaki sorumluluk dağılımı, yerel ile uzak aktarımın gidiş-dönüş farkı. Üçünde de sayılan şey bağlantının kendisiydi. Üzerinden ne geçtiği hiç açılmadı.
Bu ders o boşluğu doldurur. Model bağlam protokolünün veri katmanı (data layer) bağlantının üstünde durur ve yalnız üç mesaj türü tanır: istek, yanıt ve bildirim. Ölçülecek olan bu üç türün her birinin kaça mal olduğudur — kaç mesaj, kaç zarf belirteci, kaç gidiş-dönüş — ve bunlardan birinin, bildirimin, yanıtsız olmasının ne kazandırıp neyi görünmez kıldığıdır.
- PY31. Ortam, üç sunucu, araçlar ve zarf sayımı kurgudur; gerçek bir sunucu, istemci kitaplığı ya da çalışma zamanı çağrılmaz. 60 kurgu abone, üç kurgu sunucu, beş bildirilen araç. Tohum 20260218, ikinci koşum 20260219.
- PY32. Üç mesaj türü vardır ve her mesaj bir zarf taşır. Zarfın sabit bedeli 8 belirteçtir; gövde, alan adlarının ve değerlerin kurgu belirteçleyiciden geçmiş hâlidir.
- PY33. Bir istek yanıt bekler ve bir gidiş-dönüş harcar: yerel aktarımda bir, uzak aktarımda iki. Bildirim yanıt beklemez ve sayaçta gidiş-dönüş harcamaz.
- PY34. Yanıt kendi başına gönderilmez, bir isteğin karşılığıdır; sayaçta ayrı bir tür olarak durur çünkü zarfı ayrıca ödenir.
- PY35. Protokolsüz taban budur: araç ders içinde tanımlıdır, çağrı doğrudan bir işlev çağrısıdır, mesaj yoktur, zarf sayılmaz.
- PY36. Ölçülen toplu iş sekiz kurgu abonenin dönem bedelidir; her abone
sayac_oku,tuketimvetarifesırasından geçer. - PY37. Kurgu aktarım hattı mesajların bir payını düşürür. Kayıp payı 0,20 alınmıştır ve sayının görünür olması için yüksek tutulmuştur; düşme kararı tohumla belirlenir.
Üç Mesaj Türü
İstek ile bildirim arasındaki fark bir biçim farkı değildir, bir beklenti farkıdır. İstek gönderen taraf karşılığında bir yanıt bekler; o yanıt gelene kadar iş yarımdır ve bekleyen taraf gidiş-dönüşü öder. Bildirim gönderen taraf hiçbir şey beklemez: mesaj hatta bırakılır ve iş devam eder. Yanıt bu ikisinin arasında bağımsız bir tür değildir — kendi başına gönderilmez, ama zarfı ayrıca ödendiği için sayaçta ayrı durur.
Web API Tasarımı kursunda bir arayüzün sözleşmesi, sürümlenmesi ve hata yanıtı ölçülmüştü; o yordam burada tekrarlanmaz. Bu derste sözleşmenin içeriği değil, taşınma bedeli sayılır: aynı iş kaç mesaja bölünüyor ve her bölünme kaç belirteç ekliyor. Aşağıdaki kod kursun kurgu ortamını, üç sunucusunu ve zarf sayacını kurar.
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 (M28/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: her biri yetenek bildirir 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 anlasilan, dusen = elsikismasi() print("anlasilan arac :", " ".join(sorted(anlasilan))) print("el sikismasi :", SAYIM["istek"], "istek,", SAYIM["yanit"], "yanit,", SAYIM["belirtec"], "belirtec,", SAYIM["gidis_donus"], "gidis-donus")
anlasilan arac : sayac_oku tarife tuketim el sikismasi : 3 istek, 3 yanit, 99 belirtec, 5 gidis-donus
Zarfın Bedeli
Zarf sayacı türe göre saymayı biliyor ama gövde ile zarfı ayırmıyor. Ölçüm için mesajın çevresine ince bir sargı konur: sargı türü ve gövde belirtecini kendi dökümüne yazar, sonra işi asıl sayaca devreder. Sekiz abonelik toplu iş bu dökümle koşturulur.
TOPLU = ("A003", "A011", "A017", "A024", "A038", "A045", "A052", "A059") # ---- olcum sargisi: ayni zarf sayacinin uzerine tur basina govde belirteci DOKUM = {t: [0, 0] for t in ("istek", "yanit", "bildirim")} _zarf = mesaj def mesaj(tur, govde, aktarim="yerel"): DOKUM[tur][0] += 1 DOKUM[tur][1] += len(belirtecle(govde)) return _zarf(tur, govde, aktarim) def sifirla(): for k in SAYIM: SAYIM[k] = 0 for t in DOKUM: DOKUM[t] = [0, 0] def zincir(anlasilan, abone, ilerleme=None): """Uc araclik zincir; her adimdan sonra istege bagli bir ilerleme mesaji.""" durum = {"abone": abone} for ad in ("sayac_oku", "tuketim", "tarife"): s = cagir(anlasilan, ad, durum) durum.update({k: v for k, v in s.items() if k != "hata"}) if ilerleme is not None: ilerleme(abone, ad) return durum.get("tutar") anlasilan, _ = elsikismasi() sifirla() print("sekiz abonelik toplu is, tutarlar:", [zincir(anlasilan, a) for a in TOPLU]) print("tur sayi govde zarf toplam") for t in DOKUM: n, g = DOKUM[t] print(f"{t:<9} {n:>4} {g:>6} {n * 8:>5} {g + n * 8:>7}") print(f"protokollu : {sum(DOKUM[t][0] for t in DOKUM)} mesaj," f" {SAYIM['belirtec']} belirtec, {SAYIM['gidis_donus']} gidis-donus") print("protokolsuz : 24 islev cagrisi, 0 mesaj, 0 zarf belirteci, 0 gidis-donus")
sekiz abonelik toplu is, tutarlar: [211, 16, 844, 710, 812, 876, 694, 470] tur sayi govde zarf toplam istek 24 152 192 344 yanit 24 104 192 296 bildirim 0 0 0 0 protokollu : 48 mesaj, 640 belirtec, 32 gidis-donus protokolsuz : 24 islev cagrisi, 0 mesaj, 0 zarf belirteci, 0 gidis-donus
Son iki satır dersin ilk sayısıdır. Aynı sekiz tutar protokolsüz kurulumda 24 işlev çağrısıyla çıkıyor: mesaj yok, zarf yok, gidiş-dönüş yok. Protokollü kurulumda aynı iş 48 mesaja bölünüyor, 640 belirteç ve 32 gidiş-dönüş ediyor. Belirtecin 384’ü yalnız zarftır — türe göre 24 istekte 192, 24 yanıtta 192 — ve geriye kalan 256 belirteç gövdedir. Zarf, taşınan bilginin bir buçuk katı kadar yer tutuyor.
Bölünmenin şekli de sayıda görünüyor. İstek gövdesi 152 belirteç, yanıt gövdesi 104; istek daha
pahalı çünkü araç adını ve bütün alanları ad ile değer çiftleri hâlinde taşıyor, yanıt yalnız
sonucu taşıyor. Gidiş-dönüş 32’dir, 24 değil: sekiz zincirin ilk adımı yerel olcum sunucusuna,
kalan iki adımı uzak sunuculara gidiyor ve uzak istek gidiş-dönüşü ikiye katlıyor. Bildirim satırı
şimdilik sıfırdır; üçüncü tür bu toplu işte hiç kullanılmadı.
Bildirimin Kazandırdığı
Toplu iş uzun sürerse çağıran tarafın nerede olduğunu bilmesi istenir. Bu bilgi iki tasarımla taşınabilir. Birincisinde her adım sonunda bir istek gönderilir ve karşı taraf onaylayan bir yanıt döndürür. İkincisinde aynı metin bir bildirim olarak gönderilir ve yanıt beklenmez. İki tasarım aynı kurgu hat üzerinde, aynı tohumla, aynı düşme kararlarıyla koşturulur.
KAYIP_PAYI = 0.20 # KURGU hatta ulasmayan mesaj payi def hat(tohum): """KURGU aktarim hatti: mesajlarin bir payi karsi tarafa ulasmaz.""" r = uretec(tohum) def ulasti(): return r() >= KAYIP_PAYI return ulasti def ilerleme_olc(bicim, tohum=TOHUM): """Ayni ilerleme bilgisi iki tasarimda: istek-yanit ciftiyle ya da bildirimle.""" ulasti, ulasan = hat(tohum), [0] def bildir(abone, ad): g = f"ilerleme {abone} {ad} tamam" if bicim == "istek-yanit": mesaj("istek", g, "uzak") if ulasti(): ulasan[0] += 1 mesaj("yanit", "onay") elif bicim == "bildirim": mesaj("bildirim", g, "uzak") if ulasti(): ulasan[0] += 1 anlasilan, _ = elsikismasi() sifirla() for a in TOPLU: zincir(anlasilan, a, bildir) gonderilen = 0 if bicim == "yok" else 24 gorulen = gonderilen - ulasan[0] if bicim == "istek-yanit" else 0 return (DOKUM["istek"][0], DOKUM["yanit"][0], DOKUM["bildirim"][0], SAYIM["belirtec"], SAYIM["gidis_donus"], gonderilen, ulasan[0], gorulen) print("ilerleme tasarimi istek yanit bildirim belirtec gidis-donus" " gonderilen ulasan gorulen") for b in ("yok", "istek-yanit", "bildirim"): for t in (TOHUM, TOHUM + 1): s = ilerleme_olc(b, t) print(f"{b:<15} {'AB'[t - TOHUM]} {s[0]:>5} {s[1]:>5} {s[2]:>8} {s[3]:>8}" f" {s[4]:>11} {s[5]:>10} {s[6]:>6} {s[7]:>7}")
ilerleme tasarimi istek yanit bildirim belirtec gidis-donus gonderilen ulasan gorulen yok A 24 24 0 640 32 0 0 0 yok B 24 24 0 640 32 0 0 0 istek-yanit A 48 43 0 1147 80 24 19 5 istek-yanit B 48 44 0 1156 80 24 20 4 bildirim A 24 24 24 976 32 24 19 0 bildirim B 24 24 24 976 32 24 20 0
İlk iki satır ilerlemesiz tabandır ve iki koşumda da aynıdır. İlk koşumda istek-yanıt tasarımı toplu işin mesaj sayısını 48’den 91’e, belirteci 640’tan 1147’ye, gidiş-dönüşü 32’den 80’e çıkarıyor. Bildirim tasarımı aynı bilgiyi 72 mesaj, 976 belirteç ve 32 gidiş-dönüşle taşıyor. Bildirimin kazancı üç sütunda birden okunur: 19 mesaj, 171 belirteç ve 48 gidiş-dönüş.
Üç sayının kaynağı ayrıdır. Mesaj kazancı ulaşan isteklerin yanıtlarıdır; ilk koşumda 19 istek ulaştı, 19 onay döndü ve bildirim tasarımında bu 19 onay hiç yazılmadı. Belirteç kazancı bu 19 yanıtın zarfı ile gövdesidir, mesaj başına dokuz belirteç. Gidiş-dönüş kazancı en büyüğüdür ve ulaşma ile hiç ilgisi yoktur: 24 ilerleme mesajının hepsi uzak aktarımdan geçtiği için istek tasarımında her biri iki gidiş-dönüş harcadı, bildirimde hiçbiri harcamadı. Bekleyen bir taraf olmadığında hat üzerinde beklenen bir şey de yoktur.
Bildirimin Görünmez Kıldığı
Aynı tablonun son üç sütunu bedeli taşır. Kurgu hat ilk koşumda 24 ilerleme mesajının 19’unu geçirdi, ikinci koşumda 20’sini. İstek-yanıt tasarımında gönderen tarafın gördüğü kayıp bu farkın tamamıdır: ilk koşumda 5, ikinci koşumda 4. Bildirim tasarımında aynı hat, aynı tohum ve aynı düşme kararlarıyla aynı sayıda mesaj kayboldu, ama gönderenin gördüğü kayıp iki koşumda da sıfırdır.
Fark yanıtın kendisinden gelir. Kayıp bir istekte beklenen yanıt gelmez ve beklemenin sona ermesi gönderen tarafta bir olaydır — sayılabilir, günlüğe yazılabilir, yeniden denenebilir. Kayıp bir bildirimde beklenen bir şey yoktur; gönderen sayacı 24 der, alan tarafın sayacı 19 der ve aradaki beş mesaj hiçbir yerde hata üretmez. İki sayacı yan yana koymayan bir kurulumda bu fark görünmez.
Buradan çıkan kural mesaj türü seçimini belirler. Karşı tarafın davranmak zorunda olduğu ve gönderenin sonucunu bilmek zorunda olduğu hiçbir bilgi bildirimle taşınmaz: araç çağrısı, yetenek sorgusu, yazma işlemi. Kaybı işi bozmayan bilgi ise bildirime uygundur: ilerleme, günlük satırı, durum değişikliği duyurusu. İki koşumun farkı da bu ayrımı doğruluyor — kayıp sayısı koşumdan koşuma 5 ile 4 arasında oynuyor, ama bildirim tasarımında görülen kayıp iki koşumda da sıfır kalıyor. Değişen şey kayıptır, değişmeyen şey körlüktür.
Özet
- Veri katmanı üç mesaj türü tanır: istek yanıt bekler ve gidiş-dönüş harcar, yanıt bir isteğin karşılığıdır, bildirim yanıt beklemez ve sayaçta gidiş-dönüş harcamaz.
- Sekiz abonelik toplu iş protokolsüz kurulumda 24 işlev çağrısı, sıfır mesaj ve sıfır zarf belirteciyken protokollü kurulumda 48 mesaj, 640 belirteç ve 32 gidiş-dönüştür.
- 640 belirtecin 384’ü zarftır ve taşınan 256 belirteçlik gövdenin bir buçuk katı yer tutar.
- Aynı ilerleme bilgisi ilk koşumda istek-yanıt çiftiyle 91 mesaj, 1147 belirteç ve 80 gidiş-dönüş; bildirimle 72 mesaj, 976 belirteç ve 32 gidiş-dönüş eder. Bildirimin kazancı 19 mesaj, 171 belirteç ve 48 gidiş-dönüştür.
- Kurgu hat iki koşumda 5 ve 4 mesaj düşürdü; istek-yanıt tasarımında gönderen bu kaybın tamamını gördü, bildirim tasarımında iki koşumda da sıfırını gördü.
- Karşı tarafın davranmak zorunda olduğu ve sonucu bilinmesi gereken hiçbir bilgi bildirimle taşınmaz; bildirim yalnız kaybı işi bozmayan bilgiye uygundur.
Sonraki Adım
Bu derste mesajların bedeli sayıldı, ama sayılan mesajların hepsi anlaşılmış bir araç kümesi üzerinde çalıştı. İlk bloğun çıktısındaki tek satır bunu sessizce ele veriyor: el sıkışmasından üç araç çıktı, oysa üç kurgu sunucunun bildirdiği araç sayısı daha fazladır. Sunucunun ne bildirdiği, istemcinin bunun ne kadarını aldığı ve aradaki farkın hangi mesajda hangi hatayla oluştuğu hiç sorulmadı. Sonraki ders sunucu yeteneklerini — araç, kaynak ve istem — bildirilen ile anlaşılan arasındaki farkla ölçer ve o farkın hiçbir hata mesajı üretmeden nasıl oluştuğunu sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.