İçeriğe geç
academia.sh

Ders 05 / 14

Sohbet Sistemi

Yazmanın hâkim olduğu ilk vaka: dokuz varsayımdan çıkan bağlantı ve teslim ölçeği, teslim garantisi seçiminin kaybolan ve yinelenen ileti sayısına etkisinin bir saniyelik tepe penceresinde ölçülmesi, sıra ihlalinin ters çiftle sayılması, sıralı konvoyun bekleme turu cinsinden bedeli ve yoklamalı çekme modelinin başabaş aralığıyla elenmesi.

İçindekiler

Okuma Ağırlıklı Sistemler konusu dört vaka çıkardı ve dördünde de okuma yazmayı büyük farkla geçiyordu. Bu denge bir şeyi ucuzlatır: bir yanıtın birkaç saniye eski olması ödenebilir bir bedeldi, çünkü aynı kayıt binlerce kez okunuyor ve tazeliği tek bir yazmaya bakıyordu. Bayatlık penceresi orada bir tasarım aracıydı.

Bu konu dengenin tersini alır. Sohbette bir kayıt bir kez yazılır ve az sayıda kez okunur; kaydın kendisi bir kullanıcının gönderdiği iletidir. Kaybolması, ikinci kez görünmesi ya da yanlış sırada görünmesi kabul edilmez. Aynı kalıplar elde duruyor, ama parametreleri başka yerde duruyor.

Kısıtlar ve Kapsam

İşlevsel gereksinimler beş maddedir: bire bir ileti gönderme, en çok 50 üyeli grup iletisi, varlık bilgisi (çevrimiçi ya da son görülme), teslim ve okundu göstergesi, çevrimdışı alıcı için bekletme.

Tasarlanmayan işler açıkça yazılır: sesli ve görüntülü aktarım, dosya eki, ileti içinde arama, uçtan uca şifreleme ve geçmişin dışa aktarımı bu vakanın dışındadır.

İşlevsel olmayan gereksinimler eşikle ve eşiğin kaynağıyla yazılır: kaybolan ileti sıfırdır (kaynak: gönderene “gönderildi” gösterildikten sonra iletinin var sayılması), aynı iletinin ikinci kez gösterilmesi sıfırdır (kaynak: alıcının gördüğü listenin gönderilen listeye eşitliği), bir sohbet içinde sıra ihlali sıfırdır (kaynak: iletilerin birbirine yanıt olması), bağlı alıcıya teslim gecikmesinin ortancası 200 milisaniyeyi geçmez (kaynak: yazan tarafın karşıdaki yazma göstergesinden beklediği eşzamanlılık).

Varsayımlar ve Ölçek

Kod Varsayım Değer Gerekçe
SO1 günlük etkin kullanıcı 5.000.000 uygulamayı günde en az bir kez açan hesap
SO2 kullanıcı başına günlük gönderilen ileti 40 birkaç sohbette süren kısa alışveriş
SO3 ileti gövdesi 220 bayt metin, gönderen, sohbet kimliği, zaman damgası
SO4 tepe çarpanı 4 akşam saatlerinin gün ortalamasına oranı
SO5 eşzamanlı bağlı kullanıcı oranı 0,25 bağlantı uygulama açıkken tutulur
SO6 ileti başına alıcı 2,4 bire bir ve grup karışımının ortalaması
SO7 çevrimdışı alıcı oranı 0,35 teslim anında bağlı olmayan alıcı payı
SO8 saklama süresi 365 gün geçmişin bir yıl geriye açılması
SO9 düğüm başına bağlantı 50.000 tek düğümün taşıdığı açık bağlantı sayısı
// sohbet/olcek.mjs — SO varsayim tablosundan cikan kabaca buyukluk hesabi
export const SO = {
  gunlukKullanici: 5_000_000,    // SO1
  kullaniciBasinaIleti: 40,      // SO2
  iletiBayt: 220,                // SO3
  tepeCarpani: 4,                // SO4
  bagliOran: 0.25,               // SO5
  aliciOrt: 2.4,                 // SO6
  cevrimdisiOran: 0.35,          // SO7
  saklamaGun: 365,               // SO8
  dugumBasinaBaglanti: 50_000,   // SO9
};
const GUN = 86_400;

export function hesapla(v) {
  const gunlukIleti = v.gunlukKullanici * v.kullaniciBasinaIleti;
  const gonderimOrt = gunlukIleti / GUN;
  const gonderimTepe = gonderimOrt * v.tepeCarpani;
  const teslimTepe = gonderimTepe * v.aliciOrt;
  const bagli = v.gunlukKullanici * v.bagliOran;
  return {
    "gunluk ileti": gunlukIleti,
    "ortalama gonderim/s": gonderimOrt,
    "tepe gonderim/s": gonderimTepe,
    "tepe teslim/s": teslimTepe,
    "tepe kutu yazma/s": teslimTepe * v.cevrimdisiOran,
    "esz. bagli kullanici": bagli,
    "baglanti dugumu": bagli / v.dugumBasinaBaglanti,
    "tepe cikis Mbit/s": (teslimTepe * v.iletiBayt * 8) / 1e6,
    "gunluk veri artisi GB": (gunlukIleti * v.iletiBayt) / 1e9,
    "saklanan veri TB": (gunlukIleti * v.iletiBayt * v.saklamaGun) / 1e12,
  };
}

const r = hesapla(SO);
const bicim = (x) => (Number.isInteger(x) ? String(x) : x.toFixed(2));
for (const [ad, d] of Object.entries(r)) console.log(`${ad.padEnd(23)}${bicim(d).padStart(13)}`);

const SONUC = ["tepe teslim/s", "baglanti dugumu", "gunluk veri artisi GB"];
console.log(`\n${"iki katina cikan varsayim".padEnd(25)}${SONUC.map((s) => s.split(" ")[0].padStart(11)).join("")}`);
for (const ad of ["kullaniciBasinaIleti", "bagliOran", "aliciOrt"]) {
  const y = hesapla({ ...SO, [ad]: SO[ad] * 2 });
  console.log(`${ad.padEnd(25)}${SONUC.map((s) => `x${(y[s] / r[s]).toFixed(2)}`.padStart(11)).join("")}`);
}
gunluk ileti               200000000
ortalama gonderim/s          2314.81
tepe gonderim/s              9259.26
tepe teslim/s               22222.22
tepe kutu yazma/s            7777.78
esz. bagli kullanici         1250000
baglanti dugumu                   25
tepe cikis Mbit/s              39.11
gunluk veri artisi GB             44
saklanan veri TB               16.06

iki katina cikan varsayim       tepe   baglanti     gunluk
kullaniciBasinaIleti           x2.00      x1.00      x2.00
bagliOran                      x1.00      x2.00      x1.00
aliciOrt                       x2.00      x1.00      x1.00

Bu sayılar hesap sınıfındadır. Tasarımı dört tanesi belirliyor. Birincisi, tepe gönderim 9259,26 iken tepe teslim 22.222,22: her yazma ortalama 2,4 teslim doğuruyor ve sistemin gördüğü yük yazma değil teslim hızıdır. İkincisi, eşzamanlı 1.250.000 bağlantı 25 bağlantı düğümü demektir; bu, durumu bir yerde tutulan 25 birimdir. Üçüncüsü, tepe kutu yazma saniyede 7777,78 kayıt: teslimlerin üçte birinden fazlası bağlı olmayan bir alıcıya gidiyor ve kalıcı bir kutuya yazılıyor. Dördüncüsü, bant genişliği küçüktür: 39,11 Mbit/s çıkış, 44 GB günlük artış. Bu vakada darboğaz bayt değil kayıt sayısı ve bağlantı sayısıdır.

Duyarlılık tablosu iki kaynağı ayırıyor: bağlı oran ikiye katlandığında yalnız düğüm sayısı, alıcı sayısı ikiye katlandığında yalnız teslim hızı ikiye katlanıyor.

Tasarım

Bağlantı düğümü. Uzun ömürlü bağlantı, Trafik Katmanı kursunun Oturum Yapışkanlığı dersinde ele alınan yapışkanlığı bir tercih olmaktan çıkarıp zorunluluk yapar: bağlantı bir düğüme bağlıdır ve o düğümde kalır. Parametre yapışkanlık anahtarıdır: kullanıcı kimliği değil cihaz kimliği, çünkü aynı kullanıcının ikinci cihazı ayrı bir bağlantıdır. Cihazdan düğüme eşleme aynı kursun Dengeleme Algoritmaları dersindeki tutarlı karma ile yapılır; 25 düğümlük halkada bir düğüm eklendiğinde taşınan bağlantı payı 1/26’dır.

Yönlendirme defteri. Gönderenin düğümü, alıcının hangi düğümde bağlı olduğunu bilmek zorundadır. Bu, Uygulama Katmanı ve Servis Etkileşimi kursunun Durumsuz Servisler dersindeki durum deposudur; tuttuğu kayıt kullanıcı kimliği, düğüm adı ve son canlılık damgasıdır, yani varlık bilgisiyle aynı kayıttır.

Teslim yolu. Her teslim bir ileti aracısından geçer. Sohbet içi sıra, aynı kursun Sıralı Konvoy dersindeki şeritle korunur ve şerit anahtarı sohbet kimliğidir, gönderen kimliği değil: sıra kısıtı sohbetin içinde vardır, gönderenin bütün sohbetleri arasında değil.

Tekillik. İleti kimliğini istemci üretir ve yeniden gönderimde aynı kimlik gider. Dayanıklılık ve Güvenilirlik kursunun Etkisiz İşlemler dersindeki tekillik anahtarının kapsamı burada (gönderen, istemci ileti kimliği) ikilisidir; sohbet kimliği kapsama girmez, çünkü aynı iletinin iki sohbete gitmesi diye bir durum yoktur.

Çevrimdışı kutu. Bağlı olmayan alıcının iletileri kalıcı bir kutuya yazılır; Veri Katmanı Ölçekleme kursunun Bölümleme Stratejileri dersindeki bölüm anahtarı alıcı kimliğidir.

Bilerek kullanılmayan kalıp. Kenar önbelleği (Trafik Katmanı, Giriş Noktaları) bu tasarımda yer almaz: her ileti tek bir alıcı kümesine bir kez gider, paylaşılan bir isabet doğmaz.

Teslim Garantisi ve Sıra Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek soket, ağ ya da aracı yoktur, kanal bir olasılık ve tur soyut bir adımdır. Üç varsayım daha eklenir. SO10 — kanal kayıp olasılığı 0,02, gerekçesi hücresel bağlantının kısa kopmaları; SO11 — onay kaybı olasılığı 0,01, gerekçesi onayın dönüş yolunun daha kısa olması; SO12 — yeniden gönderim geri çekilmesi 3 tur. Model bir saniyelik tepe penceresini alır: 22.222 teslim, 40 sohbet şeridi.

// sohbet/teslim.mjs — teslim garantisi ve sira bozulmasinin surec ici modeli.
// Gercek ag, soket ya da kuyruk yoktur: kanal bir olasilik, tur soyut bir adimdir.

export function uretec(tohum) {                 // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

const TESLIM = 22222, SOHBET = 40;              // hesap: tepe teslim/s x 1 saniyelik pencere
const KAYIP = 0.02, ONAY_KAYBI = 0.01;          // SO10, SO11
const GERI_CEKILME = 3;                         // SO12: yeniden gonderim geri cekilme turu

export function kos({ deneme, defterli, konvoy, tohum }) {
  const rnd = uretec(tohum);
  const seritSonVaris = new Array(SOHBET).fill(0);
  const seritAkis = Array.from({ length: SOHBET }, () => []);
  const s = { kanalDenemesi: 0, kaybolan: 0, yinelenen: 0, beklemeTuru: 0 };

  for (let i = 0; i < TESLIM; i += 1) {
    const sohbet = i % SOHBET;
    const no = Math.floor(i / SOHBET);                 // seritte iletinin sirasi ve gonderim turu
    let tur = konvoy ? Math.max(no, seritSonVaris[sohbet]) : no;
    s.beklemeTuru += tur - no;                         // seritte onceki iletiyi bekleme
    let ulasan = 0, varis = null;
    for (let d = 0; d < deneme; d += 1) {
      s.kanalDenemesi += 1;
      if (rnd() < KAYIP) { tur += GERI_CEKILME; continue; }   // teslim kanalda dustu
      ulasan += 1;
      if (varis === null) varis = tur;
      if (rnd() < ONAY_KAYBI) { tur += GERI_CEKILME; continue; }  // onay dustu, gonderen yineler
      break;
    }
    if (ulasan === 0) s.kaybolan += 1;
    else {
      if (defterli === false) s.yinelenen += ulasan - 1;
      seritAkis[sohbet].push({ no, varis });
      seritSonVaris[sohbet] = varis;
    }
  }

  s.tersCift = seritAkis.reduce((t, akis) => {
    const gelis = akis.map((o, i) => ({ ...o, i })).sort((a, b) => a.varis - b.varis || a.i - b.i);
    let n = 0;
    for (let a = 0; a < gelis.length; a += 1)
      for (let b = a + 1; b < gelis.length; b += 1) if (gelis[a].no > gelis[b].no) n += 1;
    return t + n;
  }, 0);
  return s;
}

const DUZEN = [
  ["en cok bir kez / serbest", { deneme: 1, defterli: false, konvoy: false }],
  ["en az bir kez / serbest", { deneme: 5, defterli: false, konvoy: false }],
  ["en az bir kez + anahtar", { deneme: 5, defterli: true, konvoy: false }],
  ["ayni + sirali konvoy", { deneme: 5, defterli: true, konvoy: true }],
];

console.log(`model: ${TESLIM} teslim, ${SOHBET} sohbet seridi, kayip ${KAYIP}, onay kaybi ${ONAY_KAYBI}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(24)}${"kanal denemesi".padStart(15)}${"kaybolan".padStart(10)}` +
  `${"yinelenen".padStart(11)}${"ters cift".padStart(11)}${"bekleme turu".padStart(14)}`);
const R = {};
for (const [ad, secim] of DUZEN) {
  const r = (R[ad] = kos({ ...secim, tohum: 20260730 }));
  console.log(`${ad.padEnd(24)}${String(r.kanalDenemesi).padStart(15)}${String(r.kaybolan).padStart(10)}` +
    `${String(r.yinelenen).padStart(11)}${String(r.tersCift).padStart(11)}${String(r.beklemeTuru).padStart(14)}`);
}

const enCok = R["en cok bir kez / serbest"], enAz = R["en az bir kez / serbest"];
const konvoy = R["ayni + sirali konvoy"];
console.log(`\nkayip beklentisi = ${TESLIM} x ${KAYIP} = ${(TESLIM * KAYIP).toFixed(2)}; olculen ${enCok.kaybolan}`);
console.log(`yeniden denemenin bedeli: kanal denemesi x${(enAz.kanalDenemesi / enCok.kanalDenemesi).toFixed(4)}, ` +
  `yinelenen gosterim orani %${((100 * enAz.yinelenen) / TESLIM).toFixed(2)}`);
console.log(`konvoyun bedeli: ileti basina ${(konvoy.beklemeTuru / TESLIM).toFixed(3)} bekleme turu; ` +
  `ters cift ${enAz.tersCift} -> ${konvoy.tersCift}`);
model: 22222 teslim, 40 sohbet seridi, kayip 0.02, onay kaybi 0.01, tohum 20260730

duzen                    kanal denemesi  kaybolan  yinelenen  ters cift  bekleme turu
en cok bir kez / serbest          22222       408          0          0             0
en az bir kez / serbest           22870         0        229        827             0
en az bir kez + anahtar           22870         0          0        827             0
ayni + sirali konvoy              22870         0          0          0          1414

kayip beklentisi = 22222 x 0.02 = 444.44; olculen 408
yeniden denemenin bedeli: kanal denemesi x1.0292, yinelenen gosterim orani %1.03
konvoyun bedeli: ileti basina 0.064 bekleme turu; ters cift 827 -> 0

Bu sayılar ölçüm sınıfındadır; tohum 20260730 ile yeniden üretilir.

İlk satır en çok bir kez teslimin ne aldığını gösteriyor: bir saniyelik tepe penceresinde 408 ileti kayboluyor ve karşılığında hiçbir yineleme, hiçbir sıra ihlali doğmuyor. Beklenen kayıp 444,44’tü; ölçülen 408 bu koşumun sayısıdır. Kayıp iletiler geride kalanların sırasını bozmuyor, çünkü hiçbir şey yeniden gönderilmiyor. En çok bir kez teslimin sıra açısından temiz olması tesadüf değildir, kaybın kendisidir.

İkinci satır en az bir kez teslimin faturasını veriyor. Kayıp sıfıra iniyor; bedel kanal denemesinin 1,0292 katına çıkması, 229 iletinin alıcıya ikinci kez ulaşması (teslimlerin yüzde 1,03’ü) ve 827 ters çifttir. Yineleme onayın kaybolmasından gelir: ileti ulaşmıştır, gönderen bunu bilmez.

Üçüncü satır tekillik anahtarının payını ayırıyor: 229 yinelenen gösterim sıfıra iniyor, ters çift 827’de kalıyor. Anahtar defteri yinelemeyi çözer, sırayı çözmez; ikisi ayrı sorundur ve ayrı kalıp ister.

Dördüncü satır sıralı konvoyun payıdır: 827 ters çift sıfıra iniyor, karşılığında ileti başına 0,064 bekleme turu ödeniyor, penceredeki toplam 1414 tur. Bekleme kayıptan doğar — bir teslim düştüğünde arkasındaki iletiler şeritte bekler. Kayıpsız bir turda konvoyun bedeli sıfırdır.

Elenen Alternatif: Yoklamalı Çekme

Bağlantı tutmayan bir tasarım kurulabilir: iletiler kutuya yazılır, istemci sabit aralıkla sorar. Bu tasarım 25 bağlantı düğümünü, yönlendirme defterini ve oturum yapışkanlığını birden kaldırır. Elenme gerekçesi bir orandır.

// sohbet/alternatif.mjs — itme modeli ile yoklamali cekme modelinin sayiyla karsilastirilmasi
const BAGLI = 1_250_000, TESLIM_TEPE = 22222.22;   // olcek.mjs hesabi: bagli kullanici, tepe teslim/s
const ESIK_MS = 200;                               // teslim gecikmesi esigi (kaynagi: yazma-yaniti beklentisi)

console.log(`${"yoklama araligi".padStart(16)}${"yoklama istek/s".padStart(17)}${"itmeye orani".padStart(14)}` +
  `${"dolu yoklama".padStart(14)}${"ort. gecikme ms".padStart(17)}`);
for (const aralik of [(2 * ESIK_MS) / 1000, 1, 3, 10, 30]) {
  const istek = BAGLI / aralik;
  const dolu = Math.min(1, (TESLIM_TEPE * aralik) / BAGLI);
  console.log(`${`${aralik} s`.padStart(16)}${istek.toFixed(0).padStart(17)}` +
    `${`x${(istek / TESLIM_TEPE).toFixed(2)}`.padStart(14)}${`%${(100 * dolu).toFixed(2)}`.padStart(14)}` +
    `${((aralik * 1000) / 2).toFixed(0).padStart(17)}`);
}

const basabas = BAGLI / TESLIM_TEPE;
console.log(`\nbasabas aralik = bagli / tepe teslim = ${basabas.toFixed(2)} s`);
console.log(`bu aralikta ortalama teslim gecikmesi = ${((basabas * 1000) / 2 / 1000).toFixed(2)} s`);
console.log(`esik ${ESIK_MS} ms icin gereken aralik = ${((2 * ESIK_MS) / 1000).toFixed(1)} s, ` +
  `yani itmenin ${(BAGLI / ((2 * ESIK_MS) / 1000) / TESLIM_TEPE).toFixed(2)} kati istek`);
 yoklama araligi  yoklama istek/s  itmeye orani  dolu yoklama  ort. gecikme ms
           0.4 s          3125000       x140.63         %0.71              200
             1 s          1250000        x56.25         %1.78              500
             3 s           416667        x18.75         %5.33             1500
            10 s           125000         x5.63        %17.78             5000
            30 s            41667         x1.88        %53.33            15000

basabas aralik = bagli / tepe teslim = 56.25 s
bu aralikta ortalama teslim gecikmesi = 28.13 s
esik 200 ms icin gereken aralik = 0.4 s, yani itmenin 140.63 kati istek

Eleme sayısı son satırdır: 200 milisaniyelik ortalama gecikme eşiği 0,4 saniyelik bir aralık ister, bu da saniyede 3.125.000 istek, yani itme modelindeki teslim hızının 140,63 katıdır. Üstelik bu isteklerin yüzde 99,29’u boş döner. Yoklama, taşınmayan iş için ödenen bir hızdır.

Alternatifin kazandığı da yazılır. Başabaş aralık 56,25 saniyedir: gecikme eşiği bunun üstüne çıkarsa yoklama daha az istek üretir ve 25 bağlantı düğümü ile yönlendirme defteri tasarımdan düşer. Eleme bir teslim gecikmesi eşiği kararıdır; eşik bir dakika olsaydı seçim tersine dönerdi.

Arıza Davranışı ve Feda Edilen

Bir bağlantı düğümü düştüğünde 50.000 bağlantı aynı anda kopar ve hepsi yeniden bağlanmaya çalışır. Bu, Başarım Karşıt Kalıpları ve İzleme kursunun Yeniden Deneme Fırtınası dersindeki senaryodur; karşılığı Dayanıklılık ve Güvenilirlik kursunun Yeniden Deneme ve Fırtına Riski dersindeki sarsıntı ile deneme bütçesidir. Parametre tavandır: kalan 24 düğüme dağılan 50.000 bağlantı düğüm başına 2083 fazladan bağlantı, yani yüzde 4,17 doluluk demektir.

Aracı düştüğünde tasarım Zarif Bozulma dersindeki yolu izler: bağlantı ayakta kalır, varlık bilgisi ve okundu göstergesi durur, ileti gönderimi çevrimdışı kutuya düşer. Feda edilen tek cümleyle şudur: sohbet başına tek şerit, sohbet içi sıra uğruna sohbet düzeyinde koşutluğu ve kayıp turlarında 1414 turluk bir beklemeyi kabul eder.

Özet

  • Yazma hâkim olduğunda ölçü kayıt sayısıdır: tepe gönderim 9259,26 iken tepe teslim 22.222,22 ve bant genişliği 39,11 Mbit/s ile küçük kalıyor.
  • Eşzamanlı 1.250.000 bağlantı 25 düğüm demektir; bağlı oranı ikiye katlandığında teslim hızı değişmez, yalnız düğüm sayısı ikiye katlanır.
  • En çok bir kez teslim bir saniyelik tepe penceresinde 408 ileti kaybetti ve sırayı hiç bozmadı; en az bir kez teslim kaybı sıfırladı ama 229 yinelenen gösterim ve 827 ters çift üretti.
  • Tekillik anahtarı yinelemeyi sıfırladı, ters çifti 827’de bıraktı; sıralı konvoy ters çifti sıfırladı, ileti başına 0,064 bekleme turu aldı.
  • Yoklamalı çekme 200 milisaniyelik eşikte itmenin 140,63 katı istek ister ve bunların yüzde 99,29’u boş döner; başabaş aralık 56,25 saniyedir.
  • Kenar önbelleği bilerek kullanılmadı: her ileti tek bir alıcı kümesine gider ve paylaşılan bir isabet doğurmaz.

Sonraki Adım

Bu vakada teslim tek bir yol üzerinden yürüdü: alıcı ya bağlıydı ya da kutusuna yazıldı ve başarısızlık yeniden denemeyle karşılandı. Bir sonraki vaka aynı soruyu birden çok yolun bulunduğu yerde sorar. Aynı bildirim farklı kanallardan gidebilir, kanalların başarısızlık oranları birbirinden farklıdır ve birinin başarısızlığı ötekini denemek için gerekçedir. O zaman sorulacak sayı şudur: kanalların sırayla mı yoksa aynı anda mı denendiği toplam teslim oranını ve yeniden deneme yükünü ne kadar oynatır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat