Ders 05 / 11
Sunucu Yetenekleri
Bildirilen yetenek ile anlaşılan yetenek arasındaki farkın sayılması: üç kurgu sunucu 5 araç, 2 kaynak ve 1 istem bildiriyor, istemcinin izin listesi 4 araç taşıyor ve el sıkışmasından yalnız 3 araç çıkıyor. İki koşum aynı bedeli ödüyor — 3 istek, 3 yanıt, 99 belirteç, 5 gidiş-dönüş ve 0 hata — ama sonuçları ayrışıyor: sürümler hizalıyken 4 araç anlaşılıyor ve izin listesi kayit_yaz aracını kasten düşürüyor, sürüm sapmalıyken kayit sunucusunun tamamı düşüyor ve izin listesinde bulunan bolge_ozet aracı hiçbir hata üretmeden kayboluyor. Bir sunucunun düşmesi araçlarıyla birlikte kaynak ve istem bildirimlerini de götürüyor; olcum düşerse anlaşılan araç 2'ye iniyor. İki kayıp aynı hata metnini veriyor, biri kasıt biri kazadır.
İçindekiler
Önceki derste veri katmanının üç mesaj türü sayıldı: istek, yanıt ve bildirim. Sekiz abonelik toplu iş 48 mesaja bölündü, 640 belirteç etti ve bunun 384’ü yalnız zarftı. Ama o mesajların hepsi anlaşılmış bir araç kümesi üzerinde çalıştı ve o kümenin nasıl oluştuğu sorulmadı.
Bu ders o kümeyi kurar. Bir sunucu bağlandığında ne yapabildiğini kendisi bildirir; bu bildirim üç kalem taşır: araç, kaynak (resource) ve istem. İstemci bildirilenin tamamını almaz. Ölçülecek olan bildirilen ile anlaşılan arasındaki farktır ve bu farkın kaç mesajda, hangi hatayla oluştuğudur. Ders bir de ters yönde okunur: protokolsüz kurulumda beş aracın hepsi elin altındaydı; protokol bu kümeyi daraltıyor ve daraltmanın bir kısmı kasıt, bir kısmı kazadır.
- PY38. Ortam, üç sunucu, araçlar ve zarf sayımı kurgudur; gerçek bir sunucu, istemci kitaplığı ya da çalışma zamanı çağrılmaz. Tohum 20260218. El sıkışmasında rastgelelik yoktur: bu dersin bütün sayıları koşumdan bağımsızdır ve iki koşum, aynı sunucu kümesine bağlanan iki istemci yapılandırmasıdır.
- PY39. Yetenek bildirimi (capability declaration) üç kalem taşır: çağrılabilir araç, okunabilir kaynak ve hazır istem. Bu kursta yalnız araç çağrılır; kaynak ile istem bildirim olarak sayılır ve erişim hattı kurulmaz.
- PY40. Yetenek uzlaşması (capability negotiation) el sıkışmasında geçer: istemci her sunucuya yetenek sorar, sunucu bildirir, istemci iki süzgeçten geçirir.
- PY41. Birinci süzgeç sürüm sapmasıdır (version skew): istemcinin bildiği sürüm sunucunun sürümünden küçükse o sunucunun tamamı düşer.
- PY42. İkinci süzgeç izin listesidir: listede olmayan araç, sunucusu geçse bile alınmaz.
- PY43. İki süzgeç de el sıkışmasının yanıtından sonra çalışır; bedel ödendikten sonra daralma olur ve daralma hata üretmez.
- PY44. Protokolsüz taban budur: beş aracın beşi de ders içinde tanımlıdır, el sıkışması yoktur, hiçbir araç düşmez.
- PY45. Küme küçüktür: üç sunucu, beş bildirilen araç, dört izinli araç. Bu kümede bir oranın en küçük adımı 1/5 = 0,2000’dir; bundan küçük fark ölçülemez, bu yüzden oran yerine sayım basılır.
- PY46. Yetenek uzlaşması iki yönlüdür ve istemci de kendi yeteneklerini bildirir. Bu kurgu yalnız sunucu tarafını modeller, çünkü ölçülen şey sunucunun bildirdiğinin ne kadarının alındığıdır.
Sunucunun Bildirdiği
Bir sunucunun bildirimi bir araç listesinden ibaret değildir. Üç kalem taşır ve üçü de aynı yanıtın içinde gelir. Araç çağrılabilir bir işlevdir; bir adı, alanları ve yan etki bilgisi vardır. Kaynak okunabilir bir içeriktir; sunucunun istemciye açtığı bir belge, tablo ya da listedir. İstem hazır bir istem şablonudur; sunucu kendi alanını bilen bir metni istemciye verir. Gömme Vektörleri ve Erişim Artırılmış Üretim kursunda erişim hattının kendisi ölçülmüştü; burada o hat kurulmaz. Kaynak ile istem bu derste birer bildirim kalemi olarak sayılır, çünkü sunucu düştüğünde onlar da düşer ve bu düşüş araçla aynı sessizlikte olur.
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 print("sunucu surum aktarim arac kaynak istem") for ad, s in SUNUCU.items(): print(f"{ad:<7} {s['surum']:>5} {s['aktarim']:<8} {len(s['arac']):>5}" f" {len(s['kaynak']):>7} {len(s['istem']):>6}") print(f"bildirilen: {sum(len(s['arac']) for s in SUNUCU.values())} arac," f" {sum(len(s['kaynak']) for s in SUNUCU.values())} kaynak," f" {sum(len(s['istem']) for s in SUNUCU.values())} istem;" f" izin listesi {len(IZIN)} arac")
sunucu surum aktarim arac kaynak istem olcum 2 yerel 2 1 0 tarife 1 uzak 1 1 1 kayit 3 uzak 2 0 0 bildirilen: 5 arac, 2 kaynak, 1 istem; izin listesi 4 arac
Protokolsüz taban bu tablonun tamamıdır: araçlar ders içinde tanımlı olsaydı beşinin de elin
altında olurdu. İzin listesi dördünü onaylıyor. Aradaki tek araç kayit_yaz, tek yan etkili
araçtır ve dışarıda bırakılması bir karardır — Yapay Zekâ Ajanları kursunda aynı liste 20 çağrıyı
durdurmuştu ve o yordam burada tekrarlanmaz.
Bildirilen ile Anlaşılan
El sıkışması her sunucuya bir istek gönderir, bir yetenek yanıtı alır ve iki süzgeci yanıttan
sonra çalıştırır. Aşağıdaki iki koşumda tek fark istemcinin kayit sunucusu için bildiği
sürümdür. Web API Tasarımı kursunda sürümleme ve geriye uyum yordamı ölçülmüştü; burada
tekrarlanmaz, yalnız sapmanın araç kümesine ne yaptığı sayılır.
def sifirla(): for k in SAYIM: SAYIM[k] = 0 HIZALI = {"olcum": 2, "tarife": 1, "kayit": 3} # istemci ucuncu sunucunun surumunu de biliyor for etiket, sur in (("hizali", HIZALI), ("sapmali", ISTEMCI_SURUM)): sifirla() anlasilan, dusen = elsikismasi(sur) print(f"istemci kayit sunucusunu surum {sur['kayit']} biliyor ({etiket})") print(f" el sikismasi : {SAYIM['istek']} istek, {SAYIM['yanit']} yanit," f" {SAYIM['belirtec']} belirtec, {SAYIM['gidis_donus']} gidis-donus, 0 hata") print(f" anlasilan {len(anlasilan)} : {' '.join(sorted(anlasilan))}") for s_ad, tur, a, b in dusen: neden = f"arac {a} izin listesinde yok" if tur == "izin" else \ f"sunucu surum {a}, istemci surum {b}" print(f" dusen : {s_ad} sunucusu, {neden}")
istemci kayit sunucusunu surum 3 biliyor (hizali) el sikismasi : 3 istek, 3 yanit, 99 belirtec, 5 gidis-donus, 0 hata anlasilan 4 : bolge_ozet sayac_oku tarife tuketim dusen : kayit sunucusu, arac kayit_yaz izin listesinde yok istemci kayit sunucusunu surum 2 biliyor (sapmali) el sikismasi : 3 istek, 3 yanit, 99 belirtec, 5 gidis-donus, 0 hata anlasilan 3 : sayac_oku tarife tuketim dusen : kayit sunucusu, sunucu surum 3, istemci surum 2
İki koşumun bedeli birebir aynıdır: 3 istek, 3 yanıt, 99 belirteç, 5 gidiş-dönüş ve iki
koşumda da sıfır hata. Sonuçları ayrışıyor. Sürümler hizalıyken anlaşılan araç dört,
sapmalıyken üç. Kaybolan araç bolge_ozet, ve o araç izin listesindedir — istemci onu
kullanmayı açıkça onaylamıştır. Sunucusunun sürümü uymadığı için alınmadı.
İki düşüş satırı aynı kümede duruyor ama aynı şey değiller. kayit_yaz düşüşü kasıttır: yan
etkili araç izin listesine konmadı ve protokol bu kararı uyguladı. bolge_ozet düşüşü
kazadır: kimse bu aracı dışarıda bırakmaya karar vermedi, istemcinin bildiği sürüm numarası
eskiydi. İkisi de aynı sessizlikte oldu; el sıkışması ikisi için de sıfır hata bildirdi. Kursun
ikinci iddiasının ikinci örneği budur: standartlaştırmak da bozar. Protokolsüz kurulumda beş
araç elin altındaydı, protokollü kurulumda üç kaldı ve düşen ikisinden biri istenmeyen bir kayıptır.
Sunucu Düştüğünde Ne Gider
Sapma bir aracı değil bir sunucuyu düşürüyor. Bunun ölçüsü, her sunucunun sürümü bir eksik bilindiğinde nelerin gittiğidir. Aynı çağrı iki yapılandırmada da denenir; hata metni aynıdır.
print("dusen sunucu kaybolan arac kaynak istem kalan anlasilan arac") for ad, s in SUNUCU.items(): anlasilan, _ = elsikismasi(dict(HIZALI, **{ad: s["surum"] - 1})) print(f"{ad:<13} {len(s['arac']):>13} {len(s['kaynak']):>7} {len(s['istem']):>6}" f" {len(anlasilan):>22}") print("\nayni hata metni, iki ayri neden:") for etiket, sur in (("hizali ", HIZALI), ("sapmali", ISTEMCI_SURUM)): anlasilan, _ = elsikismasi(sur) print(f" {etiket} bolge_ozet -> {cagir(anlasilan, 'bolge_ozet', {'bolge': 'dogu'})}") print(f" {etiket} kayit_yaz -> " f"{cagir(anlasilan, 'kayit_yaz', {'abone': 'A017', 'sutun': 'bedel', 'deger': 844})}")
dusen sunucu kaybolan arac kaynak istem kalan anlasilan arac
olcum 2 1 0 2
tarife 1 1 1 3
kayit 2 0 0 3
ayni hata metni, iki ayri neden:
hizali bolge_ozet -> {'bolge': 'dogu', 'abone': 11}
hizali kayit_yaz -> {'hata': 'arac bildirilmedi'}
sapmali bolge_ozet -> {'hata': 'arac bildirilmedi'}
sapmali kayit_yaz -> {'hata': 'arac bildirilmedi'}
Tablo düşüşün birimini gösteriyor. Bir sunucunun sürümü uymadığında yalnız bir araç değil, o
sunucunun bütün bildirimi gider: olcum düşerse iki araç ve bir kaynak, tarife düşerse bir
araç, bir kaynak ve bir istem. Kaynak ile istem bu kursta çağrılmıyor, ama sayıda duruyorlar çünkü
kaybolan yetenek yalnız çağrılabilir olan değildir. En sert satır ilk satırdır: olcum yerel
aktarımda çalışan ve en çok kullanılan sunucudur, düştüğünde anlaşılan araç dörtten ikiye iner.
Son dört satır sessizliğin nasıl göründüğünü veriyor. Sürümler hizalıyken bolge_ozet çağrısı
doğu bölgesinde 11 abone döndürüyor. Sapmalı yapılandırmada aynı çağrı, aynı kod ve aynı istemle
{'hata': 'arac bildirilmedi'} veriyor — kayit_yaz çağrısının verdiği metnin aynısı. Kasıtlı
düşüş ile kazayla düşüş çağrı anında ayırt edilemez; ayırt eden tek şey el sıkışmasının düşen
listesidir, ve o liste ancak sayılırsa görünür.
Sayının biçimi de bir karardır. Anlaşılan üç araç bildirilen beşin 0,6000’idir, ama bu kümede bir oranın en küçük adımı 1/5 = 0,2000’dir: tek bir aracın gidip gelmesi oranı iki yüz birimlik adımlarla oynatır ve bundan küçük hiçbir fark ölçülemez. Bu yüzden bu derste oran değil sayım basılır — beş bildirilen, dört izinli, üç anlaşılan, bir kasıtlı düşüş, bir kazara düşüş.
Bu sayımın kime düştüğü de bir karardır. Sunucu bildirdiğini bilir, istemci aldığını bilir; ikisini yan yana koyan bir taraf yoksa fark hiçbir yerde durmaz. Kursun kuralı burada okunur: bir araç bağlantısının sayısı aracın sayısı değil sözleşmenin sayısıdır ve değiştiğinde kimin kırılacağı yazılmayan arayüz ölçülmemiş sayılır. Bu derste kırılan taraf istemcidir, kıran şey sunucunun sürüm numarasıdır ve arada hiçbir hata mesajı yoktur. İstemcinin elindeki tek çare el sıkışmasının iki ucunu saymaktır: kaç araç bildirildi, kaçı anlaşıldı, düşenlerin hangisi izin kararı hangisi sürüm sapması.
Özet
- Bir yetenek bildirimi üç kalem taşır: çağrılabilir araç, okunabilir kaynak ve hazır istem; üçü de aynı yanıtta gelir ve sunucu düştüğünde üçü birden gider.
- Üç kurgu sunucu 5 araç, 2 kaynak ve 1 istem bildiriyor; izin listesi 4 aracı onaylıyor ve el sıkışmasından 3 araç çıkıyor.
- İki yapılandırma aynı bedeli ödüyor — 3 istek, 3 yanıt, 99 belirteç, 5 gidiş-dönüş ve 0 hata — ama sürümler hizalıyken 4, sapmalıyken 3 araç anlaşılıyor.
- Sürüm sapması bir aracı değil bir sunucuyu düşürür: izin listesinde bulunan
bolge_ozethiçbir hata üretmeden kayboluyor;olcumdüşseydi anlaşılan araç 2’ye inerdi. - Düşen iki araçtan biri kasıt biri kazadır ve çağrı anında ikisi de aynı hata metnini verir; ayrımı yalnız el sıkışmasının düşen listesi gösterir.
- Beş araçlık kümede bir oranın en küçük adımı 0,2000’dir; bu yüzden oran değil sayım basılır.
Sonraki Adım
Beş derste model bağlam protokolü dışarıdan anlatıldı. Standartlaşmanın ne kazandırdığı, üç rolün sorumlulukları, aktarım katmanının gidiş-dönüş farkı, veri katmanının üç mesaj türü ve sunucunun bildirdiği yetenekler ölçüldü; her ölçünün yanına protokolsüz taban ile bedeli yazıldı. Ama bütün bu sayılar hazır kurulmuş bir kurgunun üzerinde okundu: hiçbir sunucu ve hiçbir istemci yazılmadı. Bir sunucunun yeteneğini gerçekten bildirmesi için hangi işleyicilerin yazılması gerektiği, bir istemcinin sunucuyu nasıl keşfedip oturum açtığı ve bu derste sayılan sessiz düşüşün istemci kodunda hangi satırda oluştuğu hiç sorulmadı. Sonraki konu protokolü kurmaktan gerçekleştirmeye geçer ve önce sunucu tarafını yazar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.