Üç mikroservisin birbiriyle konuşması gerekiyor ve elindeki seçenekler kulağa fazla ağır geliyor: RabbitMQ için Erlang çalışma zamanı, Kafka için üç düğüm ve disk planı, ikisi için de ayrı bir izleme yükü. Oysa senin ihtiyacın çok daha basit; servisler birbirine olay yollasın, bazen de "şunu hesapla ve cevabını bekliyorum" diyebilsin. NATS tam bu boşluğa oturur: tek bir ikili dosya, birkaç megabaytlık bellek kullanımı ve dakikalar içinde çalışan bir mesajlaşma katmanı.
Bu rehberde NATS ile hafif mesajlaşmanın nasıl kurulacağını anlatacağım. Konu (subject) hiyerarşisinin ve joker karakterlerin nasıl çalıştığını, klasik yayınla-abone ol modelinin yanında NATS'ı özel kılan istek-cevap desenini, birden fazla işçiyi aynı işe koşan kuyruk gruplarını göreceğiz. Sonra mesajın kaybolmamasını istediğinde devreye giren JetStream katmanını kuracak, üretimde en sık düşülen tuzakları — özellikle "çekirdek NATS mesajı saklamaz" gerçeğini fark etmemeyi — tek tek konuşacağız.
NATS Ne Zaman Doğru Seçimdir#
NATS'ın tasarım felsefesi tek cümleyle özetlenebilir: hız ve basitlik için dayanıklılığı varsayılan olarak vermez. Çekirdek NATS'ta bir mesaj yayınlandığında, o an dinleyen aboneler mesajı alır; kimse dinlemiyorsa mesaj hiçbir yere yazılmaz ve kaybolur. Buna "en fazla bir kez teslim" (at-most-once) denir. Kulağa kusur gibi gelir ama pek çok senaryoda tam olarak istenen davranıştır: metrik yayını, önbellek geçersizleştirme sinyali, canlı bildirim, servis keşfi gibi işlerde bir mesajın kaybolması dünyanın sonu değildir, gecikme ise kritiktir.
Mesajın kaybolmasının kabul edilemez olduğu yerlerde (ödeme, sipariş, fatura) JetStream devreye girer ve NATS'a kalıcı akışlar, onay mekanizması, yeniden teslim ve ölü mektup davranışı kazandırır. Yani NATS'ı tek bir ürün olarak değil, iki katmanlı düşünmek gerekir. Aşağıdaki tablo bu üç aracın hangi işte kendini haklı çıkardığını özetliyor:
| Kriter | NATS (çekirdek) | NATS JetStream | RabbitMQ | Kafka |
|---|---|---|---|---|
| Kurulum yükü | Tek ikili dosya | Tek ikili + disk | Erlang + broker | Küme + disk planı |
| Teslim garantisi | En fazla bir kez | En az bir kez | En az bir kez | En az bir kez |
| Tipik gecikme | Mikrosaniye seviyesi | Milisaniye | Milisaniye | Milisaniye |
| İstek-cevap | Yerleşik | Yerleşik | Elle kurulur | Uygun değil |
| Geçmişi yeniden okuma | Yok | Var | Sınırlı | Güçlü |
| Bellek ayak izi | Çok düşük | Düşük | Orta | Yüksek |
Kısacası: olay akışının geçmişini defalarca okumak istiyorsan Kafka, karmaşık yönlendirme ve öncelik kuralları istiyorsan RabbitMQ, servisler arası hızlı ve az bakım isteyen bir sinir sistemi istiyorsan NATS.
Kurulum: Tek Konteyner, Tek Komut#
NATS sunucusu tek bir ikili dosyadır ve bağımlılığı yoktur. Docker ile kaldırmak birkaç saniye sürer:
# Çekirdek NATS: 4222 istemci portu, 8222 izleme portu
docker run -d --name nats -p 4222:4222 -p 8222:8222 nats:latest \
--http_port 8222
# Sunucu ayakta mı, kaç bağlantı var?
curl -s http://localhost:8222/varz | head -20
Kalıcı akışları da kullanacaksan JetStream'i açıp veriyi bir volume'e almalısın. Compose ile daha derli toplu olur:
services:
nats:
image: nats:latest
container_name: nats
# -js JetStream'i açar, -sd depolama dizinini belirler
command: ["-js", "-sd", "/data", "-m", "8222"]
ports:
- "4222:4222" # istemci
- "8222:8222" # izleme / sağlık
volumes:
- nats-data:/data
volumes:
nats-data:
Yönetim işleri için sunucudan ayrı gelen nats komut satırı aracı gerekir; testlerin çoğunu bununla yaparsın:
# Bağlantıyı doğrula
nats --server nats://localhost:4222 server check connection
# Sunucu bilgisi
nats server info
Compose ile servis tanımlamaya ve volume mantığına yabancıysan Docker Compose kullanımı ve Docker volume veri yönetimi yazıları bu iki dosyadaki her satırı yerine oturtur. Üretimde NATS'ı doğrudan sistem servisi olarak da çalıştırabilirsin; bunun için systemd servis dosyası yazma rehberindeki şablon yeterlidir.
Konu (Subject) Hiyerarşisi ve Joker Karakterler#
NATS'ta kuyruk yaratmazsın, exchange bağlamazsın; yalnızca bir konu adına yayın yaparsın. Konular nokta ile ayrılmış hiyerarşik metinlerdir: siparis.olusturuldu, siparis.iptal, kullanici.5001.giris. Bu isimlendirme kuralı NATS'ın en güçlü tarafıdır, çünkü abonelik yaparken iki joker karakter kullanabilirsin.
* tek bir seviyeyi karşılar, > ise o noktadan sonraki tüm seviyeleri karşılar:
Yayınlanan konu siparis.* siparis.> *.olusturuldu >
------------------------------------------------------------------------
siparis.olusturuldu ✓ ✓ ✓ ✓
siparis.odeme.basarili ✗ ✓ ✗ ✓
kullanici.olusturuldu ✗ ✗ ✓ ✓
Pratikte iki terminal açıp bunu hemen deneyebilirsin:
# 1. terminal: tüm sipariş olaylarını dinle
nats sub "siparis.>"
# 2. terminal: iki farklı olay yayınla
nats pub siparis.olusturuldu '{"no":"SIP-1001","tutar":249.90}'
nats pub siparis.odeme.basarili '{"no":"SIP-1001"}'
Konu adlarını tasarlarken en genelden en özele doğru gitmek işini kolaylaştırır: alan.varlik.eylem kalıbı (siparis.fatura.olusturuldu) sonradan yeni bir dinleyici eklemeyi tek satırlık bir işe indirir. Tersine, siparisOlusturuldu gibi düz isimler kullanırsan joker karakterlerden hiç faydalanamazsın ve NATS'ı sıradan bir pub/sub aracına indirgemiş olursun.
İstek-Cevap ve Kuyruk Grupları#
NATS'ı diğer mesajlaşma araçlarından ayıran ikinci özellik, istek-cevap desenini protokol seviyesinde desteklemesidir. Bir servis "şu vergiyi hesapla" diye istek gönderir ve cevabı bekler; arka planda NATS geçici bir cevap konusu oluşturur, cevabı ona yönlendirir ve işi bitince temizler. Sen bunların hiçbirini yönetmezsin.
# 1. terminal: isteklere cevap veren bir servis taklidi
nats reply "hesapla.kdv" "KDV oranı: %20"
# 2. terminal: istek gönder, cevabı bekle (2 sn zaman aşımı)
nats request "hesapla.kdv" '{"tutar":100}' --timeout 2s
Bu desen, mikroservisler arasında HTTP yerine kullanılabilecek gerçekten hafif bir alternatiftir: servis keşfi, DNS, port yönetimi ve ters proxy katmanına ihtiyaç duymazsın. Servisin adresini değil, dinlediği konuyu bilmen yeter. Yine de HTTP'nin tamamen yerini almaz; dışa açık API'ler için bir API Gateway katmanı hâlâ gereklidir, NATS iç trafiğin dilidir.
İkinci güçlü desen kuyruk grubudur (queue group). Aynı konuya abone olan birden fazla tüketiciyi aynı gruba koyarsan, her mesaj gruptaki yalnızca bir üyeye gider. Yani klasik yayın davranışı yük dengelemeye dönüşür:
# Üç ayrı terminalde aynı grup adıyla abone ol
nats sub "is.pdf-uret" --queue isciler
# Yayınlanan her mesaj sadece BİR işçiye düşer
nats pub is.pdf-uret '{"fatura":"F-2026-0001"}'
Kuyruk grubu, ölçeklemenin en kolay yoludur: yeni bir işçi başlatmak, aynı grup adıyla abone olmaktan ibarettir. İşçi kapandığında NATS onu gruptan düşürür, kalan işçiler yükü devralır; senin hiçbir şey yapılandırman gerekmez.
JetStream ile Kalıcı Akışlar#
Çekirdek NATS'ın mesajı saklamadığını yukarıda söylemiştim. Sipariş ya da ödeme gibi kaybedilmemesi gereken olaylarda JetStream'i açar ve bir stream tanımlarsın. Stream, belirli konulardaki mesajları diske yazan kalıcı bir kayıt alanıdır:
# 'SIPARISLER' adında bir akış oluştur, siparis.> konularını yakalasın
nats stream add SIPARISLER \
--subjects "siparis.>" \
--storage file \
--retention limits \
--max-age 168h \
--max-msgs=-1 --max-bytes=5GB \
--replicas 1 --discard old --dupe-window 2m
# Akışın durumunu gör
nats stream info SIPARISLER
Stream veriyi tutar, ama okumayı consumer yapar. Kalıcı (durable) bir consumer, nereye kadar okuduğunu hatırlar; işçi yeniden başladığında kaldığı yerden devam eder:
# Kalıcı, açık onaylı bir consumer oluştur
nats consumer add SIPARISLER fatura-servisi \
--pull --ack explicit --max-deliver 5 --wait 30s \
--filter "siparis.olusturuldu" --deliver all
# Mesajları çek ve işle
nats consumer next SIPARISLER fatura-servisi --count 10
Buradaki --ack explicit ve --max-deliver 5 ikilisi kritiktir. Açık onay, mesajın ancak sen onayladığında akıştan düşmesini sağlar; onaylamazsan --wait süresi sonunda mesaj yeniden teslim edilir. --max-deliver ise bu yeniden denemenin sonsuza gitmesini engeller. Bu sınırı koymazsan bozuk bir mesaj sistemin tamamını sonsuz döngüye sokar; bu, kuyruk sistemlerinde her araç için geçerli olan aynı klasik hatadır.
| Ayar | Ne yapar | Tipik değer |
|---|---|---|
--storage | Bellek mi disk mi | file (kalıcılık için) |
--max-age | Mesajın azami yaşı | 168h (7 gün) |
--max-bytes | Akışın disk tavanı | Diskin makul bir oranı |
--ack explicit | Onaylanmayan mesaj geri gelir | Üretimde şart |
--max-deliver | Azami yeniden teslim | 3–5 |
--dupe-window | Aynı mesaj kimliğini tekrar reddet | 2m |
Sık Yapılan Hatalar ve Üretim Notları#
En pahalı hata, çekirdek NATS'ı kalıcı sanmaktır. Test ortamında her şey çalışır çünkü abone hep açıktır; üretimde işçi bir dakikalığına yeniden başladığında o aralıkta yayınlanan mesajlar sessizce kaybolur ve kimse fark etmez. Mesajın kaybolması kabul edilemezse JetStream'i açmak isteğe bağlı değildir. Konteyner sürekli yeniden başlıyorsa bu kayıp penceresi kalıcı bir soruna dönüşür; konteyner sürekli yeniden başlıyor yazısı bu döngünün kökünü bulmaya yarar.
İkinci hata, JetStream'e disk sınırı koymamaktır. --max-bytes ve --max-age vermezsen akış diski doldurana kadar büyür ve dolduğu anda yeni yayınlar reddedilmeye başlar. Üçüncüsü, kimlik doğrulamayı atlamaktır: NATS varsayılan kurulumda 4222 portunu herkese açar. Sunucuyu genel bir IP'de çalıştırıyorsan mutlaka kullanıcı/parola ya da NKey tabanlı doğrulama ve TLS tanımlamalı, portu güvenlik duvarıyla kapatmalısın:
# /etc/nats/nats.conf — asgari güvenli yapılandırma
port: 4222
http_port: 8222
jetstream {
store_dir: "/var/lib/nats"
max_memory_store: 1G
max_file_store: 20G
}
authorization {
user: servis
password: "$2a$11$..." # nats server passwd ile üretilen bcrypt hash
}
tls {
cert_file: "/etc/nats/certs/server.pem"
key_file: "/etc/nats/certs/server-key.pem"
}
Parola karmasını üretirken elle bir şey uydurma; nats server passwd komutu bcrypt karması verir. Güçlü bir parola üretmek için şifre üretici aracımızı da kullanabilirsin. Dördüncü hata izlemeyi atlamaktır: 8222 portundaki /varz, /connz ve /jsz uçları sana bağlantı sayısını, yavaş tüketicileri ve akış doluluğunu verir. Özellikle slow_consumers sayacının artması, bir abonenin mesajları yeterince hızlı çekemediğini ve NATS'ın onu düşürmeye başladığını gösterir — bu, sessizce veri kaybettiğin ilk sinyaldir.
Son olarak üretimde tek düğüm çalıştırma alışkanlığından çık. NATS küme kurmayı çok kolaylaştırır; üç düğümlü bir küme, JetStream akışlarını --replicas 3 ile çoğaltmanı sağlar ve tek makine gittiğinde sistem ayakta kalır. Üç küçük bulut sunucu örneği bu iş için fazlasıyla yeterlidir.
Sıkça Sorulan Sorular#
NATS mesajları saklıyor mu#
Çekirdek NATS mesajları saklamaz; yayınlanan mesaj yalnızca o an bağlı olan abonelere iletilir ve sonra kaybolur. Mesajların diske yazılmasını, yeniden teslim edilmesini ve geçmişe dönük okunmasını istiyorsan JetStream katmanını açman gerekir. Sunucuyu -js bayrağıyla başlatıp bir stream tanımladığında NATS kalıcı bir kuyruk sistemi gibi davranmaya başlar.
NATS mı RabbitMQ mu kullanmalıyım#
Karar, ihtiyacın olan davranışın karmaşıklığına bağlı. Servisler arası hızlı sinyalleşme, istek-cevap ve basit iş dağıtımı istiyorsan NATS çok daha az kaynak ve bakım ister; tek ikili dosyayla çalışır. Öncelikli kuyruklar, gecikmeli teslim, karmaşık yönlendirme kuralları ve olgun yönetim arayüzü istiyorsan RabbitMQ daha uygundur. İkisi de aynı sistemde farklı işler için birlikte kullanılabilir.
NATS kurulumu ne kadar sürer#
Çekirdek bir NATS sunucusunu Docker ile ayağa kaldırmak tek komut ve birkaç saniye sürer; ek bağımlılık ya da veritabanı gerekmez. JetStream'i açmak, depolama dizinini bağlamak ve ilk akışı tanımlamak da beş on dakikalık bir iştir. Asıl zaman, konu adlandırma şemasını tasarlamaya ve kimlik doğrulama ile TLS'i doğru kurmaya gider; bu ikisini baştan yapmak sonradan düzeltmekten çok daha ucuzdur.
JetStream disk alanını nasıl sınırlarım#
Her akış için --max-bytes ve --max-age değerlerini mutlaka belirle; ayrıca sunucu yapılandırmasındaki jetstream bloğunda max_file_store ile toplam tavanı koy. Sınır dolduğunda --discard old seçeneği en eski mesajları silerek yer açar, new seçeneği ise yeni yayınları reddeder. Hangisinin doğru olduğu işine bağlıdır; ölçüm verisinde eskiyi atmak, finansal olayda yeniyi reddedip alarm üretmek daha doğrudur.
NATS'ı internete açmak güvenli mi#
Varsayılan yapılandırmada güvenli değildir, çünkü kimlik doğrulama kapalıdır ve 4222 portu herkese açıktır. İnternete açacaksan TLS sertifikası tanımlamalı, kullanıcı/parola ya da NKey tabanlı doğrulama kurmalı ve mümkünse portu yalnızca bilinen IP'lere açmalısın. En temiz yaklaşım NATS'ı hiç dışa açmamak, servisleri aynı özel ağda tutmak ve dışarıya yalnızca bir API katmanı bakmasıdır.
Yavaş tüketici (slow consumer) uyarısı ne anlama geliyor#
Bu uyarı, bir abonenin kendisine gönderilen mesajları yeterince hızlı okuyamadığını ve sunucudaki tampon belleğinin dolduğunu gösterir. NATS gecikmeyi korumak için o aboneye giden mesajları düşürür ve gerekirse bağlantıyı keser. Çözüm genelde tüketicideki işi hafifletmek, işi ayrı bir çalışan havuzuna devretmek ya da kuyruk grubuna yeni işçi eklemektir; kalıcılık gerekiyorsa akışı JetStream'e taşımak doğru adımdır.
Kapanış#
NATS, "mesajlaşma karmaşık olmak zorunda değil" fikrinin somut hâli. Aklında tutman gereken dört şey var: çekirdek NATS mesajı saklamaz, saklamasını istiyorsan JetStream açarsın; konu adlarını hiyerarşik tasarlarsan joker karakterler işini yarıya indirir; kuyruk grubu ölçeklemenin en ucuz yoludur; ve üretime çıkmadan önce TLS, kimlik doğrulama ile disk sınırlarını mutlaka koyarsın.
Bu tür bir mesajlaşma katmanı, birden fazla küçük sunucunun birlikte çalıştığı mimarilerde en çok işe yarar. Kendi kümeni kurmak için düşük gecikmeli bulut sunucu ve VDS paketlerimize bakabilir, kurulumu ve güncelleme bakımını üzerinize almak istemiyorsanız sunucu yönetimi hizmetimizden yararlanabilirsiniz.