Ders 02 / 16
Durum Kodları
Durum satırının ilk basamağı bir yönergedir: aracı gövdeyi hiç açmadan saklama, yönlendirme ve yeniden deneme kararlarını ondan çıkarır.
İçindekiler
Önceki ders isteğin alanlarını ölçtü: yöntem, yol, konak. Yanıtın başlık alanlarına bakıldı ama ilk satırı bilerek dışarıda bırakıldı. Aracının bir yanıt üzerinde alacağı ilk karar o satırdadır ve satırın taşıdığı şey üç basamaklı bir sayıdır.
Bu dersin sorusu şudur: aracı, yanıtın gövdesini hiç açmadan, yalnız o sayının ilk basamağına bakarak ne kadar ileri gidebilir. Gövdeyi de açan bir aracı aynı kararlara mı varır — varıyorsa fazladan okuduğu baytın karşılığı nedir.
Durum Satırı
Yanıt, bir durum satırıyla başlar. Aşağıdaki döküm öğretim amaçlıdır ve çalıştırılmaz:
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 128 HTTP/1.1 301 Moved Permanently Location: /olcum/kuzey/guncel HTTP/1.1 503 Service Unavailable Retry-After: 30
Satırın üç parçası vardır: protokolün adı ve sürümü, üç basamaklı durum kodu, ve kodun insan okuması için yazılmış kısa karşılığı. Aracının işine yarayan tek parça ortadaki sayıdır; sağdaki metin bilgilendirmedir ve karara girmez.
Sayının kendisi de bütün olarak gerekmez. İlk basamak kodun durum sınıfını (status class) verir ve aracının okuduğu asıl şey budur.
Sınıfın Kendisi Bir Yönergedir
| Sınıf | Anlamı | Aracıya söylediği |
|---|---|---|
| 1xx | Bilgilendirme | Ara yanıt; iletilir, saklanmaz |
| 2xx | Başarı | Yanıt saklamaya uygundur |
| 3xx | Yönlendirme | Kaynağın adresi değişti |
| 4xx | İstemci hatası | Aynı istek aynı sonucu verir, yeniden denemek boşunadır |
| 5xx | Sunucu hatası | Yöntem etkisizse istek yeniden denenebilir |
Bu tablonun kritik özelliği, sağ sütunun kodun kendisini değil sınıfını kullanmasıdır. Aracı 404 ile 400 arasındaki farkı bilmek zorunda değildir; ikisi de dördüncü sınıftandır ve ikisinde de yeniden deneme boşunadır. Uygulamayı bilmeyen bir katmanın bilebileceği en üst ayrıntı düzeyi budur ve protokol o düzeyi ilk basamağa yerleştirmiştir.
Birinci sınıf, kurgunun ölçüm kümesine girmez ve bunun bir nedeni vardır: 1xx bir ara yanıttır, alışverişi bitirmez. Aracı onu ne saklar ne de üzerine karar kurar; olduğu gibi iletir ve nihai yanıtı beklemeyi sürdürür. Ölçülen şey alışveriş başına alınan karar olduğuna göre, alışverişi bitirmeyen bir yanıt sayıma giremez.
Web API Tasarımı kursunun kaynak tasarımı konusu hangi kodun seçilmesi gerektiğini ölçer: gevşek bir eşlemenin istek sayısını nasıl artırdığını sayar. Burada seçim verilidir; ölçülen şey, verilmiş bir sınıfın aracıya kaç kararı aldırdığıdır.
Üç Karar
Önceki dersin beş kararı yerinde kalır. Durum satırı üç karar daha açar:
| Karar | Gerçeği belirleyen |
|---|---|
saklamaya_uygun |
Yanıt gerçekten başarılı mı |
adres_degisti |
Kaynak gerçekten başka bir adrese mi taşındı |
yeniden_denenebilir |
Sunucu tarafında mı başarısız oldu ve yöntem etkisiz mi |
Kâhinin gerçeği bunları yanıtın gerçek sınıfından okur. Sunucunun bildirdiği kod ise ayrı bir alandır ve ikisi her zaman uyuşmaz: kurguda birkaç sunucu hatası 200 diye bildirilir. Bu, kursun her derste hatırlanan okumasının bu dersteki biçimidir — iletide yazanı doğrulayan hiçbir şey yoktur.
Karar kümesi kırk alışveriş ve sekiz karardan, yani üç yüz yirmi karardan oluşur. Ölçülebilen en küçük fark ’dir; bundan küçük bir fark iddia edilmez.
Ölçümün varsayımları:
- HA7 — Önceki dersin kırk alışverişi ve beş kararı olduğu gibi alınır; üzerine ayrı bir üreteçle durum kodu atanır ve ilk beş kararın gerçeği değişmez.
- HA8 — Durum kodları sabit bir listeden çekilir ve birinci sınıf kümeye girmez: ara yanıt alışverişi bitirmez, bitmeyen alışveriş de sayıma giremez.
- HA9 — Kâhinin gerçeği yanıtın gerçek sınıfıdır; sunucunun bildirdiği kod ayrı bir alandır ve kurguda kimi sunucu hatası 200 diye bildirilir. Bildirimi doğrulayan bir düzenek yoktur.
- HA10 — İki rejim karşılaştırılır: aracı ya yalnız durum satırını okur, ya gövdeyi de açar. Aradaki tek fark sınıfın nereden okunduğudur — kodun ilk basamağından ya da gövdeden.
- HA11 — Gövde, çözülmüş bir yanıt içeriğini temsil eden sabit yapıda bir alandır ve gerçek sınıfı doğru taşır; gövdeyi açmanın baytı bu alanın sayımından gelir.
- HA12 — Tur bilançosu bir zamanlama ölçümü değil bir sayımdır: üçüncü sınıf yanıtın eklediği ve dördüncü sınıf yanıtın sildiği turlar alışveriş sayısından hesaplanır.
Çekirdeğin Genişletilmesi
Çekirdek önceki dersinkiyle aynıdır; üzerine durum_ata işlevi, üç yeni karar ve gövdeyi
gören ikinci bir görünürlük demeti eklenir. kahin işlevinin ilk beş satırına
dokunulmamış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"] DURUMLAR = [200, 200, 200, 201, 204, 301, 400, 404, 500, 503, 503] TEMEL = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir", "yonlendirilebilir") DURUM_KARARLARI = ("saklamaya_uygun", "adres_degisti", "yeniden_denenebilir") KARARLAR = TEMEL + DURUM_KARARLARI ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici") SATIR = ACIK + ("durum",) SATIR_GOVDE = SATIR + ("govde",) 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, tohum: int = TOHUM) -> 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 durum_ata(liste: list[dict], tohum: int = TOHUM + 101) -> list[dict]: r = uretec(tohum) for a in liste: kod = DURUMLAR[r(11)] gercek = kod // 100 yanilti = gercek == 5 and r(3) == 0 # sunucu, hatayı 200 diye bildirdi a["gercek_sinif"] = gercek a["durum"] = 200 if yanilti else kod a["yanilti"] = yanilti 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, "saklamaya_uygun": a["gercek_sinif"] == 2, "adres_degisti": a["gercek_sinif"] == 3, "yeniden_denenebilir": a["gercek_sinif"] == 5 and a["yontem"] in ETKISIZ, } 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"], "durum": a["durum"], "govde": {"sinif": a["gercek_sinif"], "yol": a["yol"], "ayrinti": "cozumlenmis yanit govdesi", "izleme": f"iz-{a['no']:04d}"}} return {k: v for k, v in tam.items() if k in gorunur} def bayt(i: dict) -> int: return sum(len(k) + len(str(v)) + 4 for k, v in i.items()) 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 if "durum" in i: sinif = i["govde"]["sinif"] if "govde" in i else i["durum"] // 100 k["saklamaya_uygun"] = sinif == 2 k["adres_degisti"] = sinif == 3 if "yontem" in i: k["yeniden_denenebilir"] = sinif == 5 and i["yontem"] in ETKISIZ return k def olc(alis: list[dict], gorunur: tuple, kararlar: tuple = KARARLAR) -> tuple: d = y = e = b = 0 for x in alis: gercek, verilen = kahin(x), araci(ileti(x, gorunur)) for karar in kararlar: if karar not in verilen: e += 1 elif verilen[karar] == gercek[karar]: d += 1 else: y += 1 b += bayt(ileti(x, gorunur)) return d, y, e, b def dagilim(alis: list[dict], alan: str) -> dict: d: dict = {} for x in alis: d[x[alan]] = d.get(x[alan], 0) + 1 return dict(sorted(d.items()))
araci işlevindeki tek satır bütün ayrımı taşır: gövde görünürse sınıf gövdeden, görünmezse
durum // 100 ile satırdan okunur. İki aracı arasındaki fark yalnız budur.
İki Aracı
alis = durum_ata(alisverisler()) print("gerçek sınıf dağılımı:", dagilim(alis, "gercek_sinif")) print("bildirilen kod dağılımı:", dagilim(alis, "durum")) print("sınıfını yanlış bildiren yanıt:", sum(x["yanilti"] for x in alis)) t = olc(alis, SATIR, TEMEL) print(f"temel beş karar (iki rejimde de aynı): doğru {t[0]}, yanlış {t[1]}") print() print(f"{'rejim':<12s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} {'bayt':>6s}") for ad, g in (("satır", SATIR), ("satır+gövde", SATIR_GOVDE)): d, y, e, b = olc(alis, g) print(f"{ad:<12s} {d:6d} {y:7d} {e:11d} {b:6d}")
gerçek sınıf dağılımı: {2: 22, 3: 3, 4: 7, 5: 8}
bildirilen kod dağılımı: {200: 16, 201: 5, 204: 4, 301: 3, 400: 4, 404: 3, 500: 1, 503: 4}
sınıfını yanlış bildiren yanıt: 3
temel beş karar (iki rejimde de aynı): doğru 195, yanlış 5
rejim doğru yanlış alınamayan bayt
satır 309 11 0 4004
satır+gövde 315 5 0 8044
İlk okunacak sütun alınamayandır ve iki rejimde de sıfırdır. Durum satırı görünür olduğu sürece aracı üç yeni kararın üçünü de alır; sunucuya sormaz, tur borçlanmaz. Önceki dersin beş kararı da yerinde durur: iki rejimde de 195 doğru, 5 yanlış.
Aradaki bütün fark üç yüz yirmi kararın altısındadır. Satır rejimi 309, gövdeyi de açan rejim 315 doğru karar verir. Buna karşılık gövdeyi açmak baytı 4004’ten 8044’e çıkarır.
Gövdenin Kazandırdığı
Altı kararın nereden geldiğine bakmak gerekir.
print(f"{'':<20s} {'satır':>14s} {'satır+gövde':>16s}") print(f"{'karar':<20s} {'doğru':>6s} {'yanlış':>7s} {'doğru':>8s} {'yanlış':>7s}") for karar in DURUM_KARARLARI: s = [0, 0, 0, 0] for x in alis: gercek = kahin(x) for j, g in enumerate((SATIR, SATIR_GOVDE)): v = araci(ileti(x, g)) s[2 * j + (0 if v[karar] == gercek[karar] else 1)] += 1 print(f"{karar:<20s} {s[0]:6d} {s[1]:7d} {s[2]:8d} {s[3]:7d}") print() print(f"{'no':>3s} {'yöntem':<7s} {'yol':<14s} {'gerçek':>7s} {'bildirilen':>11s} " f"{'etkisiz':>8s}") for x in alis: if x["yanilti"]: print(f"{x['no']:3d} {x['yontem']:<7s} {x['yol']:<14s} {x['gercek_sinif']:7d} " f"{x['durum']:11d} {str(x['yontem'] in ETKISIZ):>8s}") ds, dg = olc(alis, SATIR), olc(alis, SATIR_GOVDE) kazanc, bedel = dg[0] - ds[0], dg[3] - ds[3] print() print(f"gövdenin kazandırdığı karar {kazanc}, ödettiği bayt {bedel}, " f"karar başına {bedel / kazanc:.1f} bayt") print("karar kümesi iki rejimde ayrılan alışveriş:", sum(1 for x in alis if araci(ileti(x, SATIR)) != araci(ileti(x, SATIR_GOVDE)))) eklenen = sum(x["gercek_sinif"] == 3 for x in alis) kacinilan = sum(x["gercek_sinif"] == 4 for x in alis) print(f"yönlendirmenin eklediği tur {eklenen}, boşuna denemeden kaçınılan tur " f"{kacinilan}, kırk alışverişte net {eklenen - kacinilan}")
satır satır+gövde karar doğru yanlış doğru yanlış saklamaya_uygun 37 3 40 0 adres_degisti 40 0 40 0 yeniden_denenebilir 37 3 40 0 no yöntem yol gerçek bildirilen etkisiz 2 PUT /olcum/yamac 5 200 True 8 GET /olcum/kuzey 5 200 True 9 GET /ozet 5 200 True gövdenin kazandırdığı karar 6, ödettiği bayt 4040, karar başına 673.3 bayt karar kümesi iki rejimde ayrılan alışveriş: 3 yönlendirmenin eklediği tur 3, boşuna denemeden kaçınılan tur 7, kırk alışverişte net -4
Kırk alışverişin otuz yedisinde iki aracı aynı karar kümesini üretir. Gövdeyi açmak bu otuz yedide tek bir kararı bile değiştirmez; yalnız bayt okur. Ayrılan üç alışverişte kazanılan karar sayısı altı, ödenen bayt 4040 — karar başına 673,3 bayt.
Karşılaştırma ölçüsü de burada belirir. Durum satırı, aracıya kırk alışverişin kırkında üç kararı birden verir ve bunun için alışveriş başına yalnız on iki bayt okutur. Gövde, üç yüz yirmi kararın altısını düzeltir ve bunun için alışveriş başına yüz bir bayt daha okutur. Sınıfın ilk basamağa konması bir yer kazanma tercihi değildir; aracının okuması gereken en küçük veriyi yanıtın en başına koyma tercihidir.
Karar Bedava, İşlem Bir Tur
adres_degisti kararı iki rejimde de kırk kararın kırkında doğrudur; dersin en ucuz kararı
budur. Ucuzluk, kararın sonucunun da ucuz olduğu anlamına gelmez.
Üçüncü sınıf bir yanıt istenen içeriği vermez, yalnız başka bir adres bildirir. Aracı bunu iletiden okur ve kimseye sormaz — karar fazladan tur istemez. Ama kararın gereğini yapmak, yani yeni adrese gitmek, tam bir gidiş–dönüştür. Kırk alışverişin üçü üçüncü sınıftandır ve bu üç alışveriş hiçbir başlık alanının kaldıramayacağı üç tur ekler; turu kaldırmanın tek yolu adresi değiştirmektir.
Dördüncü sınıf ters yönde çalışır. Aracı bir 4xx gördüğünde aynı isteğin yeniden gönderilmesinin aynı sonucu vereceğini bilir ve göndermez. Kırk alışverişin yedisi dördüncü sınıftandır; bu yedi alışverişte tek bir basamak, sonucu değişmeyecek bir turu peşinen atmaktan kurtarır. Kırk alışverişteki bilanço eksi dörttür: yönlendirme üç tur ekler, istemci hatası yedi turu siler.
Beşinci sınıfın bilançosu yönteme bağlıdır ve bu yüzden tek başına yazılamaz. Sekiz alışverişin kaçının yeniden denenebileceğini belirleyen şey durum sınıfı değil, önceki dersin ölçtüğü yöntem alanıdır; karar iki alanın kesişiminde durur.
Bu üç satır, ölçünün neden alan sayısı değil tur olduğunu gösterir. Durum satırı tek bir alandır ve gövdeye hiç dokunmadan kırk alışverişte üç tur ekler, yedi tur siler.
Sınıfını Yanlış Bildiren Yanıt
Üç alışverişte sunucu, sunucu tarafında oluşan bir hatayı 200 diye bildirmiştir. Gövdede gerçek durum yazılıdır; satırda yazmaz.
Satır rejiminde bu üç yanıt iki kararı birden bozar. saklamaya_uygun üçünde de yanlış
çıkar: aracı başarısız bir yanıtı önbelleğine koyar ve aynı hatayı sonraki isteklere de
dağıtır. yeniden_denenebilir de üçünde yanlış çıkar, çünkü üçünün de yöntemi etkisizdir
ve gerçek sınıfı bilinseydi istek yeniden gönderilebilirdi. adres_degisti bozulmaz: yanlış
bildirim üçüncü sınıfa dokunmaz.
Buradaki asıl sonuç şudur: aracı yanlış karar vermedi, yanlış bildirime doğru davrandı. İlk basamağı okumak, ilk basamağın doğru yazıldığı varsayımıyla birlikte gelir. Protokolde bu varsayımı sınayan bir düzenek yoktur; gövdeyi açmak da bir çözüm değildir, çünkü gövdeyi yazan da aynı sunucudur.
Daraltma tarafı da vardır. Sunucu durum satırını doğru yazdığında aracı hiçbir şey kaybetmez; yanlış yazdığında ise kaybı gövdeyle kapatmak alışveriş başına yüz bayttan fazla tutar ve kırk alışverişin otuz yedisinde bu bayt boşa gider. Sınıfı doğru bildirmek, aracının okuması gereken veriyi en aza indiren tek şeydir.
Özet
- Durum satırının ilk basamağı bir durum sınıfı verir; aracı saklama, yönlendirme ve yeniden deneme kararlarını kodun tamamından değil bu tek basamaktan çıkarır.
- Sınıf tabanlı okuma, aracının kodun ayrıntısını bilmesini gerektirmez: 400 ile 404 aynı yönergeyi taşır.
- Kırk alışveriş ve sekiz kararlık kümede satır rejimi 309 doğru, 11 yanlış karar ve 4004 bayt; gövdeyi de açan rejim 315 doğru, 5 yanlış karar ve 8044 bayt verir.
- İki rejim kırk alışverişin otuz yedisinde aynı karar kümesini üretir; gövdenin kazandırdığı altı kararın bedeli karar başına 673,3 bayttır.
- Kararın ucuz olması işlemin de ucuz olduğunu göstermez: yönlendirme kırk alışverişte üç tur ekler, istemci hatası yedi turu siler, bilanço eksi dörttür.
- Sınıfını yanlış bildiren üç yanıt
saklamaya_uygunveyeniden_denenebilirkararlarını bozar; aracı yanlış karar vermez, yanlış bildirime doğru davranır.
Sonraki Adım
Durum satırı yanıtın ne olduğunu söyler. Söylemediği bir şey vardır: aynı adresin birden çok karşılığı olabilir ve sunucu bunlardan hangisini gönderdiğini durum satırında bildirmez. Aynı ölçüm kaydı ayrı biçimlerde, ayrı dillerde, ayrı kodlamalarda sunulabilir; ikisi de 200 ile döner. Sonraki ders bu seçimin yapıldığı yeri, yani başlık alanlarını ölçer ve iki soruyu birden sorar: ilgisiz alanlar eklemek aracıya karar kazandırır mı, ve seçimin hangi alana bakılarak yapıldığı bildirilmezse aracı hangi kopyayı kime verir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.