Ders 12 / 16
Sunucu Gönderimli Olaylar
Akışın HTTP kalmasının aracıya ne bıraktığı: baş iletinin taşıdığı beş karar, olay sayısı arttıkça karar noktasının seyrelmesi, bir kez alınan kararın gövdenin tamamına uygulanmasının doğurduğu yanlış ve tamponlayan bir aracının olayları ne kadar geciktirdiği.
İçindekiler
Önceki ders tünelin bedelini saydı: yükseltmeden sonra aracı iki yüz kararın tamamını kaybediyor, kırk alışverişin kırkı tur borcuna dönüşüyordu. Ama sunucunun istemciye kendiliğinden veri göndermesi için bağlantının biçimini değiştirmek zorunlu değildir. HTTP yanıtının gövdesinin tek seferde yazılması hiçbir yerde şart koşulmaz: gövde açık tutulup üzerine parça parça yazılabilir.
Sunucu gönderimli olaylar (Server-Sent Events, SSE) bu seçeneği bir biçime bağlar. Tek bir GET isteğine verilen tek bir yanıt açık tutulur ve sunucu ona sırayla olay yazar. Bu dersin ölçüsü şudur: akış HTTP kaldığı için aracı iletiyi hâlâ okuyabilir — peki elinde kaç karar kalır ve okuyabilmenin karşılığında ne öder?
Akış Yanıtı
İstek olağandır. Yanıt da olağandır; onu ayıran tek şey ortam türü (media type) ve gövdenin bitmemesidir.
GET /akis/olcum HTTP/1.1
Host: istasyon.ornek
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-store
Transfer-Encoding: chunked
event: olcum
id: 17
data: {"istasyon": "kuzey-yamac", "deger": 3}
: yorum satırı hiçbir olay üretmez
event: olcum
id: 18
data: {"istasyon": "kuzey-yamac", "deger": 5}
Gövde satır tabanlıdır: alan satırları bir olayı kurar, boş satır onu kapatır, iki nokta
ile başlayan satır yorumdur. Uzunluk önceden bilinemediği için gövde parçalı aktarım
(chunked transfer) ile ya da bağlantının kapanmasıyla sonlanır. Alan biçiminin ayrıntısı,
id alanına dayanan yeniden bağlanma ve sunucu tarafının bağlantı başına ödediği maliyet
Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü. Burada ölçülen,
aradaki aracının bu iletiden alabildiği karardır.
Karar Noktası
- DG6. Önceki derste bir bağlantı üzerinde taşınan kırk alışveriş, burada bir akışın
gövdesinde taşınır.
olay, tek bir akışın gövdesine yazılan alışveriş sayısıdır;olay = 1her alışverişin kendi isteğini açtığı istek–yanıt biçimidir. - DG7. Aracı kararını akışın baş iletisinden alır. Gövdeye yazılan olaylar kendi istek satırını ve başlık alanlarını taşımaz; aracı için görünmezdirler.
- DG8.
kapsam yanlışısütunu ikinci bir okumadır: baş iletiden alınan karar gövdedeki her olaya uygulandığında kaç kararın yanlış olduğunu sayar. - DG9. Son satır, iletinin hiçbir alanının okunamadığı tünel rejimidir; önceki dersin ölçümüyle karşılaştırma için buraya alınmıştır.
TOHUM = 20260809 GUVENLI = {"GET", "HEAD"} ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"} YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"] YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"] KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir", "yonlendirilebilir") ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici") def uretec(tohum: int): d = tohum % 2147483646 + 1 def r(n: int) -> int: nonlocal d d = (d * 48271) % 2147483647 return d % n return r def alisverisler(sayi: int = 40) -> list[dict]: r, liste = uretec(TOHUM), [] for i in range(sayi): yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)] ozel = yol == "/oturum" or r(5) == 0 degisti = r(3) == 0 liste.append({"no": i + 1, "yontem": yontem, "yol": yol, "ozel": ozel, "degisti": degisti, "ozel_imi": ozel and r(4) != 0, "damga_atladi": degisti and r(6) == 0}) return liste def kahin(a: dict) -> dict: return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"], "paylasilabilir": not a["ozel"], "taze": not a["degisti"], "yinelenebilir": a["yontem"] in ETKISIZ, "yonlendirilebilir": True} def ileti(a: dict, gorunur: tuple) -> dict: tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek", "ozel_imi": a["ozel_imi"], "dogrulayici": a["degisti"] and not a["damga_atladi"]} return {k: v for k, v in tam.items() if k in gorunur} def araci(i: dict) -> dict: k = {} if "yontem" in i: k["yinelenebilir"] = i["yontem"] in ETKISIZ if "ozel_imi" in i: k["paylasilabilir"] = not i["ozel_imi"] if "yontem" in i: k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"] if "dogrulayici" in i: k["taze"] = not i["dogrulayici"] if "konak" in i: k["yonlendirilebilir"] = True return k def olc_akis(olay: int, gorunur: tuple = ACIK) -> tuple: liste = alisverisler() nokta = dogru = yanlis = alinamayan = tur = bayt = kapsam = 0 for i, a in enumerate(liste): m = ileti(liste[i - i % olay], gorunur) # akışı açan iletinin alanları bastan, gercek = araci(m), kahin(a) kapsam += sum(bastan[k] != gercek[k] for k in bastan) verilen = bastan if i % olay == 0 else {} # karar yalnız akış başında alınır dogru += sum(verilen[k] == gercek[k] for k in verilen) yanlis += sum(verilen[k] != gercek[k] for k in verilen) alinamayan += len(KARARLAR) - len(verilen) tur += 1 if len(verilen) < len(KARARLAR) else 0 if i % olay == 0: nokta += 1 bayt += sum(len(k) + len(str(v)) + 4 for k, v in m.items()) return nokta, dogru, yanlis, alinamayan, tur, bayt, kapsam bicim = "{:>5s} {:>6d} {:>6d} {:>7d} {:>11d} {:>4d} {:>6d} {:>14d}" print(f"{'olay':>5s} {'nokta':>6s} {'doğru':>6s} {'yanlış':>7s} " f"{'alınamayan':>11s} {'tur':>4s} {'bayt':>6s} {'kapsam yanlışı':>14s}") for olay in (1, 2, 4, 8, 20, 40): print(bicim.format(str(olay), *olc_akis(olay))) print(bicim.format("-", *olc_akis(40, gorunur=()))) liste = alisverisler() bas = araci(ileti(liste[0], ACIK)) print() print("olay=40 kapsamında karar başına yanlış (40 olay üzerinden):") for k in KARARLAR: y = sum(bas[k] != kahin(a)[k] for a in liste) print(f" {k:<18s} baş iletideki değer {str(bas[k]):<5s} yanlış {y:2d}/40")
olay nokta doğru yanlış alınamayan tur bayt kapsam yanlışı
1 40 195 5 0 0 3524 5
2 20 97 3 100 20 1747 37
4 10 47 3 150 30 879 45
8 5 23 2 175 35 436 57
20 2 9 1 190 38 179 71
40 1 4 1 195 39 87 73
- 1 0 0 200 40 0 0
olay=40 kapsamında karar başına yanlış (40 olay üzerinden):
saklanabilir baş iletideki değer False yanlış 13/40
paylasilabilir baş iletideki değer True yanlış 17/40
taze baş iletideki değer True yanlış 12/40
yinelenebilir baş iletideki değer False yanlış 31/40
yonlendirilebilir baş iletideki değer True yanlış 0/40
İlk satır istek–yanıt biçimidir ve kursun ölçtüğü açık rejimin ta kendisidir: 195 doğru, 5 yanlış, hiç alınamayan karar yok, hiç tur borcu yok, 3524 bayt. Son satır önceki dersin tünel rejimidir: 0 karar, 200 alınamayan, 40 tur borcu.
Aradaki satırlar akışın ne yaptığını gösterir. Kırk olay tek bir gövdeye yazıldığında aracı bir karar noktasında kalır ve elinde beş karar bulunur. Bu, tünelin sıfırından beş karar fazladır ve fark tam olarak şuradan gelir: akışı açan yanıt okunabilir bir HTTP yanıtıdır. Önceki derste 101 yanıtı kişiye özel imi de doğrulayıcı da taşımadığı için sıfır karar veriyordu; burada yanıt olağan bir 200’dür ve beş kararın beşini de besler.
Baytın düşüşü de aynı yerden gelir. 3524 bayttan 87 bayta inen şey, kırk iletinin başlık alanlarının otuz dokuzunun hiç gönderilmemesidir. Alan gitmiş, karar da gitmiştir; bu, önceki dersin sonucuyla aynı yöndedir. Bayt ile karar birlikte düşer, çünkü ikisini de taşıyan şey alandır.
Kararın Kapsamı
Karar noktasının seyrelmesi kayıpların tamamı değildir. Bir aracı, akışın başında aldığı kararı unutmaz — onu gövdenin tamamına uygular. Yanıtı saklanabilir saydıysa gövdenin tümünü saklar; taze saydıysa gövdenin tümünü taze sayar.
kapsam yanlışı sütunu bunu ölçer. olay = 1 iken baş ileti ile olay aynı şeydir ve
sütun beş yanlışı verir. olay = 40 iken tek bir karar kümesi kırk olaya uygulanır ve
iki yüz kararın 73’ü yanlış çıkar. Karar noktası birden kırka çıkmamıştır; kararın
kapsamı bire kırktan yayılmıştır.
Alt döküm 73’ün nereden geldiğini söyler. yonlendirilebilir hiç yanılmaz, çünkü değeri
her alışverişte aynıdır; kapsamın genişlemesi değişmeyen bir kararı bozmaz.
yinelenebilir ise 31 kez yanılır: baş ileti bir POST’tur ve kararı False çıkmıştır, oysa
gövdedeki olayların çoğu güvenli yöntemlere aittir. Kalan üçü — saklanabilir 13,
paylasilabilir 17, taze 12 — kişiye özellik ve bayatlığın olaydan olaya değişmesinden
gelir. Kural şudur: kapsamın genişlemesi, yalnız alışverişten alışverişe değişen
kararları bozar.
Bu, akışın en sessiz maliyetidir. Karar noktasında ölçüldüğünde aracının yalnız bir yanlışı vardır ve bu, açık rejimin beş yanlışından azdır. Aynı akış kapsamında ölçüldüğünde yanlış sayısı beşten 73’e çıkar. Kümenin çözünürlüğü 1/200 = 0,0050 olduğuna göre bu fark ölçülebilir bandın çok üstündedir ve yorum farkından değil, kararın ne kadar süre geçerli sayıldığından gelir. Akış yanıtının önbelleklenmeye kapatılmasının nedeni budur.
Yeniden Bağlanma Bir Karar Noktasıdır
Tablodaki olay sütunu tek bir soruyu daha yanıtlar. Bir akış hiçbir zaman sonsuza kadar
ayakta kalmaz; bağlantı kopar, istemci yeniden bağlanır. Yeniden bağlanma yeni bir istek,
yeni bir istek ise yeni bir karar noktasıdır. Yirmi olayda bir kopan bir akış tablonun
olay = 20 satırıdır: iki karar noktası, on karar. Sekiz olayda bir kopan akış beş nokta ve
yirmi beş karar verir. Aracının gördüğü şey, akışın ne kadar kararlı olduğuyla ters
orantılıdır.
Bunun bir sonucu yönlendirmedir. yonlendirilebilir kararı akışın başında bir kez alınır
ve akış boyunca dondurulur. İstek–yanıt biçiminde aracı kırk alışverişin her birini
ayrı ayrı yönlendirebilir; bir üst kaynak yanıt vermediğinde sonraki isteği başka bir yere
gönderir. Tek bir akışta bu esneklik yoktur: seçim bir kez yapılmıştır ve değiştirmenin tek
yolu akışı kesmektir. Kesmek de yeni bir karar noktası doğurur, yani aracının yönlendirmeyi
düzeltmesi ancak istemcinin yeniden bağlanmasıyla mümkün olur.
Kopmanın ucuz olmasını sağlayan alan — istemcinin en son gördüğü olay kimliğini yeni isteğe iliştirmesi — bu tabloda hiçbir karar üretmez. Aracı o alanı okur ama ondan çıkaracağı bir şey yoktur; alan uçlar arasındaki bir sözleşmedir. Bu, kursun dört numaralı okumasının bir başka örneğidir: bir alanın var olması, aracının ondan karar çıkarabildiği anlamına gelmez.
Tamponlama
Aracı iletiyi okuyabildiği için gövdeyi de görür, ve gördüğü şey üzerinde bir seçim yapar: baytı hemen mi iletsin, yoksa biriktirip toplu mu göndersin. Biriktirmek olağan bir verimlilik kuralıdır; bitmeyen bir gövdede ise olayları geciktirir.
- DG10. Aracı, tamponu eşiğe ulaşmadan gövdeden hiçbir bayt iletmez. Eşik 0 ise tamponlama yoktur. Olay metinleri belirtimin satır biçimiyle üretilir, değerler kurgudur.
def olay_metni(no: int, deger: int) -> str: """Belirtimin satır biçimi: alan satırları ve olayı kapatan boş satır.""" return (f"event: olcum\nid: {no}\n" f'data: {{"istasyon": "kuzey-yamac", "deger": {deger}}}\n\n') OLAYLAR = [olay_metni(i + 1, i * 3 % 97) for i in range(40)] def tamponla(esik: int) -> tuple: tampon, grup, bekleme = 0, [], [] for i, m in enumerate(OLAYLAR): tampon += len(m) grup.append(i) if tampon >= esik: bekleme.extend(i - j for j in grup) # her olayın kaç olay beklediği tampon, grup = 0, [] ortalama = sum(bekleme) / len(bekleme) if bekleme else 0.0 return len(bekleme), max(bekleme, default=0), ortalama, len(grup) print(f"olay sayısı {len(OLAYLAR)}, olay boyu " f"{min(len(m) for m in OLAYLAR)}-{max(len(m) for m in OLAYLAR)} bayt, " f"gövde {sum(len(m) for m in OLAYLAR)} bayt") print() print(f"{'eşik':>6s} {'iletilen':>9s} {'en çok bekleme':>15s} " f"{'ortalama bekleme':>17s} {'sonda tutulan':>14s}") for esik in (0, 128, 256, 1024, 4096): iletilen, en_cok, ortalama, tutulan = tamponla(esik) ec = str(en_cok) if iletilen else "-" ort = f"{ortalama:.2f}" if iletilen else "-" print(f"{esik:6d} {iletilen:9d} {ec:>15s} {ort:>17s} {tutulan:14d}")
olay sayısı 40, olay boyu 66-68 bayt, gövde 2704 bayt
eşik iletilen en çok bekleme ortalama bekleme sonda tutulan
0 40 0 0.00 0
128 40 1 0.50 0
256 40 3 1.50 0
1024 32 15 7.50 8
4096 0 - - 40
İki bakışla okunur. İlk üç satırda gecikme eşikle orantılı büyür: 256 baytlık bir tampon olayları ortalama 1,5 olay geciktirir. Son satır ise farklı bir şeydir. Akışın kırk olayda ürettiği toplam gövde 2704 bayttır ve 4096 baytlık bir eşik bu gövdenin tamamından büyüktür; aracı hiçbir baytı iletmez ve akış, kapanana kadar istemci için hiç var olmamıştır.
Kritik olan şudur: aracı yanlış bir şey yapmıyor. Bir yanıtı biriktirmek, bitmiş bir gövde
için doğru davranıştır. Aracının bilmediği şey gövdenin bitmeyeceğidir ve bunu ona söyleyen
tek alan ortam türüdür. text/event-stream değerini tanımayan bir aracının elinde
başka hiçbir im yoktur: gövde uzunluğu bildirilmemiştir, yöntem GET’tir, durum kodu 200’dür
ve bunların hiçbiri “bu gövde bitmeyecek” demez. Sunucu tarafının kalp atışı satırıyla
aldığı önlem, aynı boşluğun öbür uçtan kapatılmasıdır ve o kurs içinde ölçülmüştü.
Tek Yön
Akış tek yönlüdür: sunucudan istemciye. İstemcinin sunucuya söyleyeceği bir şey olduğunda akışa yazamaz, olağan bir istek açar. Bu, ölçüm tablosunda kayıp gibi görünmeyen bir ayrıntıdır ama sonucu doğrudandır: istemciden gelen her ileti yeniden bir karar noktasıdır. Akış tarafında karar noktası bire inmiştir; istemcinin sunucuya yazdığı her istek ise ölçüm tablosunun ilk satırındaki gibi okunabilir bir alışveriştir ve kendi karar noktasını geri getirir.
Tünelde bu bile yoktur; oradaki her iki yönlü ileti aynı çerçeve biçimindedir ve hiçbiri karar üretmez. Aradaki fark, seçimin ne üzerinden yapıldığını da gösterir: iki yön gerçekten gerekliyse tünel, gereken şey yalnız sunucu akışıysa SSE. Yönün bedeli karar cinsinden ölçülebilir.
Özet
- Sunucu gönderimli olaylar HTTP’nin içinde kalır: istek olağan bir GET, yanıt olağan bir 200’dür ve gövde açık tutulup üzerine olay yazılır.
- Kırk olay tek bir akışta taşındığında aracının karar noktası bire iner ve elinde beş karar kalır; tünel rejiminde bu sayı sıfırdır ve fark, akışı açan yanıtın okunabilir olmasından gelir.
- Baş iletiden alınan karar gövdenin tamamına uygulanır; karar noktasında ölçülen yanlış sayısı 1 iken kapsamda ölçülen yanlış sayısı iki yüz kararın 73’üdür.
- Bayt 3524’ten 87’ye inerken karar da 200’den 5’e iner; ikisini de taşıyan şey alandır.
- Tamponlayan bir aracı 256 baytlık eşikte olayları ortalama 1,5 olay geciktirir; 2704 baytlık gövdeye karşılık 4096 baytlık bir eşikte hiçbir olay iletilmez ve gövdenin bitmeyeceğini söyleyen tek alan ortam türüdür.
Sonraki Adım
Akış da tünel de bağlantıyı açık tutarak çalışıyor: biri gövdeyi bitirmiyor, öbürü protokolü değiştiriyor. İkisi de sunucunun elini serbest bırakmak için bağlantının süresini uzatıyor. Peki hiçbirini yapmadan, protokole tek bir yeni alan bile eklemeden, yalnız daha sık sorarak nereye kadar gidilir? Sonraki ders bunu ölçer: kısa yoklama, uzun yoklama ve sunucu akışı aynı olay kümesi üzerinde yan yana konur ve bölen olay değil, karar olur.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.