---
title: 'Servis Ağı'
source: 'https://academia.sh/tr/kurslar/servis-mimarileri/servis-agi'
course: 'Servis Mimarileri'
language: tr
updated: '2026-08-17T18:06:53+00:00'
license: 'CC BY-SA 4.0'
---

# Servis Ağı

Ağ ilgilerinin uygulama kodundan çıkarılması: yeniden deneme, zaman aşımı, güvenli taşıma ve ölçüt toplamanın yan arabaya taşınmasıyla silinen satırın sayılması, geriye kalan sorumluluğun adlandırılması ve ayrı süreçte koşmanın ödettiği durak, süreç ile alan bilgisi kaybının ölçülmesi.

Önceki dersin bıraktığı yerde üç toplama katmanı vardı ve üçünün de dış çağrısı çıplaktı: ne
zaman aşımı, ne yeniden deneme, ne sayaç. Gerçek bir sistemde bu üç işin her servis kodunda
tekrar yazılması gerekir. Kütüphane ödünç sisteminde ödünç servisi ücret servisine gidiyor;
gidiş bir ağ üzerinden, taşıma güvenli, karşı taraf zaman zaman geçici hata döndürüyor.

Bu dersin sorusu şu: yeniden deneme, zaman aşımı, güvenli taşıma ve ölçüt toplama uygulama
kodundan çıkarılıp ana süreçle birlikte koşan ayrı bir sürece verilirse, uygulama kodundan kaç
satır silinir, geriye ne kalır ve bunun bedeli nedir. Yan araba ile elçinin tanımı Trafik
Katmanı kursunda kuruldu ve orada dağıtım birimi düzeyinde ölçüldü; burada ölçü kod tarafıdır.
Her servisin yanında böyle bir süreç koştuğunda ve hepsinin politikası tek bir yerden
verildiğinde ortaya çıkan yerleşime **servis ağı** (service mesh), politikanın verildiği yere
**denetim düzlemi** (control plane) denir.

Varsayımlar: **SS13** ücret servisi güvenli taşıma ile konuşur ve sertifika ölçüm sırasında
üretilir. **SS14** ücret servisi ilk iki okuma isteğini geçici hatayla reddeder; ödeme ucu
kaydı işler ama ilk yanıtı kaybolmuş sayılır. **SS15** ölçüm süre ölçmez; satır, süreç, durak
ve tahsilat sayar.

## Ücret Servisi

Ücret servisinin iş ucu güvenli taşıma üzerinde. Yanına düz bir yönetim ucu konuldu; o uç
ölçüm düzeneğidir, sayaçları okur ve sıfırlar.

```js
// mesh/ucret.mjs — ucret servisi: is ucu 8751 guvenli tasima, yonetim ucu 8757 duz
import { createServer as tls } from "node:https";
import { createServer } from "node:http";
import { readFileSync } from "node:fs";

const sayac = { okuma: 0, odeme: 0, tahsil: 0 };
const sifirla = () => Object.keys(sayac).forEach((k) => (sayac[k] = 0));

tls({ key: readFileSync("mesh/anahtar.pem"), cert: readFileSync("mesh/sertifika.pem") },
  (q, y) => {
    if (q.url === "/ceza/ode") {
      sayac.odeme += 1;
      sayac.tahsil += 1;                       // kayit islenir; ilk yanit yolda kaybolmus sayilir
      const kod = sayac.odeme === 1 ? 503 : 200;
      y.writeHead(kod, { "Content-Type": "application/json" });
      return y.end(JSON.stringify({ tahsil: sayac.tahsil, tutar: 12.5 }));
    }
    sayac.okuma += 1;
    const kod = sayac.okuma <= 2 ? 503 : 200;  // ilk iki okuma istegi gecici olarak reddedilir
    y.writeHead(kod, { "Content-Type": "application/json" });
    y.end(JSON.stringify(kod === 200 ? { borc: 12.5 } : { hata: "gecici" }));
  }).listen(8751, "127.0.0.1", () => console.log("ucret 127.0.0.1:8751"));

createServer((q, y) => {
  if (q.url === "/sifirla") sifirla();
  y.writeHead(200, { "Content-Type": "application/json" });
  y.end(JSON.stringify(sayac));
}).listen(8757, "127.0.0.1");
```

## Ağ İlgileri Uygulama Kodunda

İlk gerçekleştirimde ödünç servisi her şeyi kendisi yapıyor. Ağ ilgisi taşıyan satırlar
işaretli; ölçüm bu işareti sayacak.

```js
// mesh/odunc-kalin.mjs — butun ag ilgileri uygulama kodunda, 8752
import { createServer } from "node:http";
import { request } from "node:https";                                   // [tls]
import { readFileSync } from "node:fs";                                 // [tls]

const CA = readFileSync("mesh/sertifika.pem");                          // [tls]
const ZAMAN_ASIMI = 400;                                                // [zaman asimi]
const DENEME = 3;                                                       // [yeniden deneme]
const olcut = {};                                                       // [olcut]

const cagir = (yol, yontem) => new Promise((coz, red) => {              // [tls]
  const q = request({ host: "127.0.0.1", port: 8751, path: yol,         // [tls]
    method: yontem, ca: CA }, (y) => {                                  // [tls]
    let g = "";
    y.on("data", (p) => (g += p));
    y.on("end", () => coz({ kod: y.statusCode, govde: g }));
  });
  q.setTimeout(ZAMAN_ASIMI, () => q.destroy(new Error("zaman asimi"))); // [zaman asimi]
  q.on("error", red);                                                   // [zaman asimi]
  q.end();
});

const guvenli = (yontem) => yontem === "GET";     // yalniz etkisiz cagri yinelenir [yeniden deneme]
const dene = async (yol, yontem, ad) => {
  const o = (olcut[ad] ??= { istek: 0, deneme: 0, hata: 0 });            // [olcut]
  o.istek += 1;                                                         // [olcut]
  let son;
  for (let i = 0; i < (guvenli(yontem) ? DENEME : 1); i += 1) {         // [yeniden deneme]
    o.deneme += 1;                                                      // [olcut]
    son = await cagir(yol, yontem);
    if (son.kod < 500) break;                                           // [yeniden deneme]
  }
  if (son.kod >= 500) o.hata += 1;                                      // [olcut]
  return son;
};

const json = (y, kod, g) => {
  y.writeHead(kod, { "Content-Type": "application/json" });
  y.end(JSON.stringify(g));
};

createServer(async (q, y) => {
  const [kok, uye] = q.url.split("/").filter(Boolean);
  if (kok === "olcut") return json(y, 200, olcut);                       // [olcut]
  const is = kok === "ode" ? { yol: "/ceza/ode", yontem: "POST" }
    : { yol: `/ceza/${uye}`, yontem: "GET" };
  const s = await dene(is.yol, is.yontem, kok);
  json(y, s.kod, { islem: kok, kod: s.kod, govde: s.govde });
}).listen(8752, "127.0.0.1", () => console.log("odunc-kalin 127.0.0.1:8752"));
```

## Aynı İş, İnce Uygulama ve Yan Araba

İkinci gerçekleştirimde iş mantığı satır satır aynı kalıyor; değişen tek şey `dene` işlevinin
gövdesi. Uygulama artık yerel bir adrese düz bir çağrı yapıyor.

```js
// mesh/odunc-ince.mjs — ag ilgileri uygulama disinda, 8753: yerel adrese duz cagri
import { createServer } from "node:http";

const dene = (yol, yontem) => fetch(`http://127.0.0.1:8754${yol}`, { method: yontem })
  .then(async (y) => ({ kod: y.status, govde: await y.text() }));

const json = (y, kod, g) => {
  y.writeHead(kod, { "Content-Type": "application/json" });
  y.end(JSON.stringify(g));
};

createServer(async (q, y) => {
  const [kok, uye] = q.url.split("/").filter(Boolean);
  const is = kok === "ode" ? { yol: "/ceza/ode", yontem: "POST" }
    : { yol: `/ceza/${uye}`, yontem: "GET" };
  const s = await dene(is.yol, is.yontem, kok);
  json(y, s.kod, { islem: kok, kod: s.kod, govde: s.govde });
}).listen(8753, "127.0.0.1", () => console.log("odunc-ince 127.0.0.1:8753"));
```

Silinen satırların hepsi yan arabaya taşınıyor. Yan arabanın gördüğü şey bir yol, bir yöntem ve
bir durum kodudur; hangi işin yapıldığını bilmez.

```js
// mesh/yan-araba.mjs — yan araba 8754: zaman asimi, yeniden deneme, guvenli tasima, olcut
import { createServer } from "node:http";
import { request } from "node:https";
import { readFileSync } from "node:fs";

const CA = readFileSync("mesh/sertifika.pem");
const ZAMAN_ASIMI = 400;
const DENEME = 3;
const YINELENEN_KOD = [502, 503, 504];      // politika durum kodudur; isin ne oldugu bilinmez
const olcut = {};

const cagir = (yol, yontem) => new Promise((coz, red) => {
  const q = request({ host: "127.0.0.1", port: 8751, path: yol, method: yontem, ca: CA }, (y) => {
    let g = "";
    y.on("data", (p) => (g += p));
    y.on("end", () => coz({ kod: y.statusCode, govde: g }));
  });
  q.setTimeout(ZAMAN_ASIMI, () => q.destroy(new Error("zaman asimi")));
  q.on("error", red);
  q.end();
});

createServer(async (q, y) => {
  if (q.url === "/olcut" || q.url === "/sifirla") {
    const g = JSON.stringify(olcut);
    if (q.url === "/sifirla") Object.keys(olcut).forEach((a) => delete olcut[a]);
    y.writeHead(200, { "Content-Type": "application/json" });
    return y.end(g);
  }
  const o = (olcut[q.url] ??= { istek: 0, deneme: 0, hata: 0 });
  o.istek += 1;
  let son;
  for (let i = 0; i < DENEME; i += 1) {
    o.deneme += 1;
    son = await cagir(q.url, q.method);
    if (!YINELENEN_KOD.includes(son.kod)) break;
  }
  if (son.kod >= 500) o.hata += 1;
  y.writeHead(son.kod, { "Content-Type": "application/json" });
  y.end(son.govde);
}).listen(8754, "127.0.0.1", () => console.log("yan-araba 127.0.0.1:8754"));
```

## Ölçüm

Ölçüm iki yerleşimde de aynı iki işi koşuyor: bir borç sorgusu ve bir ceza ödemesi. Sonra
ücret servisinin sayaçlarını, iki taraftaki ölçütleri ve kod satırlarını okuyor.

```js
// mesh/olc.mjs — iki yerlesim ayni isi kosar: sonuc, deneme, tahsil ve silinen satir
import { readFileSync } from "node:fs";

const al = async (u) => JSON.parse(await (await fetch(u)).text());
const kod = async (u) => (await fetch(u)).status;
const anlamli = (d) => readFileSync(d, "utf8").split("\n").map((s) => s.trim())
  .filter((s) => s && !s.startsWith("//") && /[A-Za-z]/.test(s));
const ilgi = (d) => readFileSync(d, "utf8").split("\n")
  .filter((s) => /\/\/ \[/.test(s)).length;                 // ag ilgisi olarak isaretli satir

const YERLESIM = [["kalin uygulama", 8752, null], ["ince + yan araba", 8753, 8754]];
const sonuc = [];
for (const [ad, uygulama, arac] of YERLESIM) {
  await al("http://127.0.0.1:8757/sifirla");            // ucret sayaclari
  if (arac) await al(`http://127.0.0.1:${arac}/sifirla`);
  const borc = await kod(`http://127.0.0.1:${uygulama}/borc/U-41`);
  const ode = await kod(`http://127.0.0.1:${uygulama}/ode/U-41`);
  const sayac = await al("http://127.0.0.1:8757/sayac");
  const olcut = await al(`http://127.0.0.1:${arac ?? uygulama}/olcut`);
  sonuc.push({ ad, borc, ode, sayac, olcut, surec: arac ? 3 : 2, durak: arac ? 2 : 1 });
}

const B = ["borc kodu", "odeme kodu", "ucrete ulasan", "tahsil", "surec", "yerel durak"];
console.log(`${"yerlesim".padEnd(18)}${B.map((b) => b.padStart(14)).join("")}`);
for (const s of sonuc) {
  console.log(`${s.ad.padEnd(18)}${[s.borc, s.ode, s.sayac.okuma + s.sayac.odeme,
    s.sayac.tahsil, s.surec, s.durak].map((v) => String(v).padStart(14)).join("")}`);
}
console.log("");
for (const s of sonuc) {
  const d = Object.entries(s.olcut).map(([a, o]) => `${a} ${o.istek}/${o.deneme}/${o.hata}`);
  console.log(`${s.ad.padEnd(18)} olcut (istek/deneme/hata): ${d.join("  ")}`);
}

const SERVIS = 5;                        // katalog, uyelik, odunc, ucret, bildirim
const k = anlamli("mesh/odunc-kalin.mjs"), i = anlamli("mesh/odunc-ince.mjs");
const y = anlamli("mesh/yan-araba.mjs");
const silinen = k.filter((s) => !i.includes(s));
console.log(`\nodunc-kalin = ${k.length} satir, odunc-ince = ${i.length} satir, ` +
  `silinen = ${silinen.length}, eklenen = ${i.filter((s) => !k.includes(s)).length}`);
console.log(`ag ilgisi diye isaretli satir: uygulama kodunda ${ilgi("mesh/odunc-kalin.mjs")} -> 0`);
console.log(`${SERVIS} servis icin ag ilgisi satiri: uygulama tarafinda ${SERVIS * silinen.length}, ` +
  `yan araba tarafinda ${y.length} (tek dosya)`);
```

```bash
openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj "/CN=localhost" \
  -addext "subjectAltName=IP:127.0.0.1" -keyout mesh/anahtar.pem -out mesh/sertifika.pem 2>/dev/null
node mesh/ucret.mjs > /dev/null &
node mesh/odunc-kalin.mjs > /dev/null &
node mesh/odunc-ince.mjs > /dev/null &
node mesh/yan-araba.mjs > /dev/null &
for p in 8752 8753 8754 8757; do
  curl -s --retry 30 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:$p/olcut
done
node mesh/olc.mjs
kill $(jobs -p)
```

```
yerlesim               borc kodu    odeme kodu ucrete ulasan        tahsil         surec   yerel durak
kalin uygulama               200           503             4             1             2             1
ince + yan araba             200           200             5             2             3             2

kalin uygulama     olcut (istek/deneme/hata): borc 1/3/0  ode 1/1/1
ince + yan araba   olcut (istek/deneme/hata): /ceza/U-41 1/3/0  /ceza/ode 1/2/0

odunc-kalin = 38 satir, odunc-ince = 13 satir, silinen = 28, eklenen = 3
ag ilgisi diye isaretli satir: uygulama kodunda 18 -> 0
5 servis icin ag ilgisi satiri: uygulama tarafinda 140, yan araba tarafinda 33 (tek dosya)
```

## Silinen Satır ve Kalan Sorumluluk

Uygulama kodu 38 anlamlı satırdan 13'e iniyor; 28 satır siliniyor, yerine 3 satır geliyor.
Silinen satırların 18'i ağ ilgisi diye işaretliydi ve uygulama kodunda bu işaretten hiç
kalmadı. Ölçünün asıl anlamı çarpanda: aynı 28 satır beş servisin her birinde yazılacağı için
uygulama tarafındaki toplam 140 satırdır; yan araba tarafında ise 33 satırlık tek bir dosya
vardır ve politikası dışarıdan verilir.

Geriye kalan sorumluluk üç maddedir ve hiçbiri yan arabaya devredilemez. Birincisi, hangi yola
hangi yöntemle gidileceği: `/ceza/ode` ucunun `POST` olduğu bilgisi uygulamadadır. İkincisi,
dönen durum kodunun iş açısından ne demek olduğu — 503 gören uygulama kullanıcıya ne
gösterecektir. Üçüncüsü ve en önemlisi, bir çağrının yinelenmesinin güvenli olup olmadığı.
Kalın gerçekleştirimde bu bilgi `guvenli` işlevinde tek satırdır; ince gerçekleştirimde o satır
silindi ve yerine hiçbir şey konmadı.

Bedel iki sütunda görünüyor. Süreç sayısı 2'den 3'e, isteğin dokunduğu yerel durak 1'den 2'ye
çıkıyor. Ücret servisine ulaşan istek 4'ten 5'e çıkıyor. Bu, Trafik Katmanı kursunun dağıtım
birimi başına süreç ölçüsünün kod tarafındaki karşılığıdır: aynı iş, bir durak fazlasıyla.

Ölçüt satırları kaybı gösteriyor. Kalın yerleşimde sayaçlar `borc` ve `ode` diye, yani işin
adıyla tutuluyor; ince yerleşimde `/ceza/U-41` ve `/ceza/ode` diye, yani ücret servisinin yol
sözlüğüyle. Yan araba "borç sorgusu" ile "ceza ödemesi" arasındaki farkı yalnız yola bakarak
tahmin edebilir. Üye numarası yola girdiği için her üye ayrı bir ölçüt anahtarı üretir; alan
bilgisi olmadan bu anahtarları toplamak da uygulama dışında bir işe dönüşür.

## Yeniden Denemenin Bilmediği Şey

Tablonun en pahalı sütunu `tahsil` sütunudur. İki yerleşim de aynı iki isteği yaptı; kalın
yerleşimde ücret servisi bir tahsilat işledi, ince yerleşimde iki tahsilat işledi.

Nedeni politikanın konumudur. Ücret servisinin ödeme ucu kaydı işliyor ve ilk yanıtı 503
olarak dönüyor — yanıtın yolda kaybolduğu durumun taklidi. Kalın uygulamada `guvenli` işlevi
`POST` çağrısını yinelemeye kapatmıştı: ödeme bir kez denendi, uygulamaya 503 döndü, tahsil 1
oldu. Yan araba ise yalnız durum koduna bakıyor; 503 gördü, aynı isteği yineledi, ikinci
denemede 200 aldı ve uygulamaya 200 döndürdü. Ödünç servisi işlemin başarılı olduğunu sanıyor;
üyenin cezası iki kez tahsil edilmiş durumda ve hiçbir tarafta hata kaydı yok.

Doğan arıza kipi budur: **ağ katmanında verilen bir yeniden deneme kararı, iş katmanında
yinelemeye kapalı olan bir çağrıyı yineler.** Kalın yerleşimde böyle bir arıza kipi yoktu,
çünkü karar veren kodun elinde çağrının ne olduğu bilgisi vardı.

Çözüm, sorumluluğu bir ölçüde geri çağırmaktır. Yan arabanın politikası yönteme göre
daraltılabilir ya da uygulama isteğe bir tekillik anahtarı koyup ücret servisi yinelenen
tahsilatı düşebilir. İkisi de bedava değildir: birincisi ağ politikasının içine alan bilgisi
koyar, ikincisi ücret servisine defter tutma işi ekler. Ölçülen sonuç şudur: ağ ilgileri
uygulamadan çıkarılabilir, ama **hangi çağrının yinelenebilir olduğu** bilgisi çıkarılamaz;
yalnız yer değiştirir.

## Özet

- Aynı iş iki yerleşimde koşturuldu: uygulama kodu 38 satırdan 13 satıra indi, 28 satır silindi
  ve 3 satır eklendi; ağ ilgisi diye işaretli 18 satırdan uygulamada hiçbiri kalmadı.
- Beş servisli sistemde aynı ilgi uygulama tarafında 140 satır tutar; yan araba tarafında 33
  satırlık tek dosya vardır ve politikası dışarıdan verilir.
- Uygulamada kalan sorumluluk: yol ile yöntem seçimi, durum kodunun iş anlamı ve bir çağrının
  yinelenmesinin güvenli olup olmadığı.
- Ödenen bedel: süreç 2'den 3'e, yerel durak 1'den 2'ye, ücret servisine ulaşan istek 4'ten
  5'e; ölçüt anahtarları işin adından yol sözlüğüne düştü.
- Doğan arıza kipi: durum koduna bakan yeniden deneme, yinelemeye kapalı ödeme çağrısını
  yineledi; tahsilat 1 yerine 2 oldu ve iki tarafta da hata kaydı oluşmadı.

## Sonraki Adım

İki gerçekleştirimin de sessiz bir ortak kabulü vardı: ücret servisinin adresi `127.0.0.1:8751`
diye kodun içinde yazılıydı. Kalın uygulamada bu adres uygulama dosyasında, ince uygulamada yan
araba dosyasında duruyor; iki durumda da tek bir sabittir. Oysa bir servisin tek kopyası
olmayabilir, kopyalar gelip gidebilir ve giden bir kopyanın adresi bir süre daha geçerliymiş
gibi durabilir. Konunun son dersi adresin kodun dışından çözülmesini ele alır: bir kayıt defteri
süreci kurar, kayıtlara bir kira süresi verir, sağlık bilgisini yönlendirmeye bağlar ve bunu
sabit yapılandırmayla iki sayı üzerinden karşılaştırır — bir kopya eklendiğinde dağıtımda
dokunulan dosya ve bir kopya öldüğünde ölü adrese giden istek.
