Eksiksiz NATS Geliştirici Rehberi
NATS ile üretim (production) sistemleri kurmak için derinlemesine ve pratik bir kaynak — Core NATS, JetStream, güvenlik, kümeleme (clustering) ve kanıtlanmış tasarım desenlerini (design pattern) kapsar.
İçindekiler
- NATS Nedir
- Temel Kavramlar
- Core NATS Mesajlaşma
- Subject’ler ve Wildcard’lar
- Queue Group’lar
- Request-Reply
- JetStream’e Genel Bakış
- Stream’ler
- Consumer’lar
- Key/Value Store
- Object Store
- Güvenlik
- Kümeleme ve Supercluster’lar
- İzleme ve Gözlemlenebilirlik
- İstemci Kütüphaneleri
- Tasarım Desenleri (Patterns)
- En İyi Uygulamalar Kontrol Listesi
- Sık Yapılan Hatalar
- Dağıtım (Deployment) Örnekleri
- Kaynaklar
1. NATS Nedir
NATS, Go dilinde yazılmış, basitlik ve hız üzerine tasarlanmış, hafif ve yüksek performanslı bir mesajlaşma sistemidir. Şunları sağlar:
- Core NATS: gönder-ve-unut (fire-and-forget) pub/sub, request-reply, kuyruklama — en fazla bir kez (at-most-once) teslimat, son derece düşük gecikme (mikrosaniye seviyesi), kalıcılık (persistence) yok.
- JetStream: Core NATS üzerine inşa edilmiş, en az bir kez (at-least-once) ve tam olarak bir kez (exactly-once) teslimat, akış (streaming), tekrar oynatma (replay) ve kalıcı (durable) consumer’lar sağlayan yerleşik bir kalıcılık katmanı — Kafka’ya benzer ama işletmesi çok daha kolay.
- NATS.io ekosistemi: NATS Server, NGS (global yönetilen servis), leaf node’lar, supercluster’lar, NATS CLI, NATS Surveyor, NACK (Kubernetes controller).
Neden Kafka/RabbitMQ yerine NATS?
| Özellik | NATS | Kafka | RabbitMQ |
|---|---|---|---|
| Operasyonel karmaşıklık | Çok düşük (tek binary) | Yüksek (ZK/KRaft, broker’lar) | Orta |
| Gecikme (latency) | Milisaniye altı | Düşük-ms | Düşük-ms |
| Kalıcılık | Opsiyonel (JetStream) | Her zaman | Opsiyonel |
| Protokol | Basit metin tabanlı | Özel binary | AMQP |
| Çoklu kiracılık (multi-tenancy) | Account (yerleşik) | ACL’ler | vhost’lar |
| Edge / IoT uyumluluğu | Evet (leaf node, minik ayak izi) | Hayır | Kısmi |
| Exactly-once | Evet (JetStream + dedup) | Evet (transaction’lar) | Hayır (native olarak) |
2. Temel Kavramlar
- Subject: adresleme mekanizması (topic gibi düşünün), örn.
orders.created.eu. Hiyerarşik, nokta ile ayrılmış. - Publisher/Subscriber: subject’lere yayın yapan/subject’leri dinleyen taraflar.
- Connection: bir istemcinin NATS sunucusuna olan TCP bağlantısı; birçok sub/pub’ı çoğullayabilir (multiplex).
- Server / Cluster / Supercluster: tek node → kümelenmiş node’lar (aynı bölge) → cluster’ları birbirine bağlayan gateway’ler (çoklu bölge).
- Account: kendi subject alanına, güvenlik bağlamına ve JetStream limitlerine sahip izole bir isim alanı — çoklu kiracılığın (multi-tenancy) temel yapı taşı.
- Stream: bir veya birden fazla subject’ten yakalanan mesajların JetStream tarafından yönetilen, kalıcı, sıralı günlüğü (log).
- Consumer: bir stream üzerindeki imleç/görünüm; push veya pull tabanlı olabilir, teslimat durumunu takip eder.
3. Core NATS Mesajlaşma
Publish / Subscribe
nc, _ := nats.Connect(nats.DefaultURL)
defer nc.Close()
sub, _ := nc.Subscribe("orders.created", func(msg *nats.Msg) {
fmt.Printf("Alındı: %s\n", string(msg.Data))
})
defer sub.Unsubscribe()
nc.Publish("orders.created", []byte(`{"id": 123}`))
Core NATS en fazla bir kez (at-most-once) teslimat sağlar: dinleyen bir subscriber yoksa mesaj kaybolur. Kalıcılık, onaylama (ack) veya tekrar oynatma (replay) yoktur. Bu bilinçli bir tasarım kararıdır — geçici sinyaller, telemetri, servis keşfi (service discovery) ve düşük gecikmeli RPC için kullanın. Kalıcılık gerektiğinde JetStream kullanın.
Bağlantı Yaşam Döngüsü ve Yeniden Bağlanma
Production ortamında yeniden bağlanma davranışını her zaman açıkça yapılandırın:
nc, err := nats.Connect(
"nats://server1:4222,nats://server2:4222,nats://server3:4222",
nats.MaxReconnects(-1), // sonsuza kadar dene
nats.ReconnectWait(2*time.Second),
nats.Timeout(5*time.Second),
nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
log.Printf("bağlantı koptu: %v", err)
}),
nats.ReconnectHandler(func(nc *nats.Conn) {
log.Printf("yeniden bağlanıldı: %s", nc.ConnectedUrl())
}),
nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("bağlantı kapatıldı")
}),
)
Bağlantı ilk kurulurken client’ın diğer node’ları keşfedebilmesi için her zaman tek bir URL yerine birden fazla seed URL verin.
4. Subject’ler ve Wildcard’lar
Subject’ler nokta ile ayrılmış token’lardır: region.service.event, örn. eu.billing.invoice_created.
*tam olarak bir token ile eşleşir:eu.*.invoice_created,eu.billing.invoice_createdile eşleşir amaeu.billing.sub.invoice_createdile eşleşmez.>bir veya daha fazla sondaki token ile eşleşir (son sırada olmalı):eu.>, hemeu.billing.invoice_createdhem deeu.billing.sub.xile eşleşir.
Subject Tasarımı için En İyi Uygulamalar
- Subject’leri geniş’ten dar’a doğru hiyerarşik tasarlayın:
<domain>.<entity>.<event>.<region>— bu, consumer’ların ihtiyaç duydukları herhangi bir granülaritede subscribe olmasına imkan tanır. - Yüksek kardinaliteli dinamik veriyi asla baştaki token olarak koymayın — örn.
<user_id>.eventsyapmayın;events.user.<user_id>tercih edin, böyleceevents.>üzerindeki wildcard subscription’lar mantıklı kalır ve ACL’ler yönetilebilir olur. - Subject’leri kısa ve öngörülebilir tutun — uzun subject’ler ölçekte eşleştirme (matching) için daha fazla CPU maliyetine yol açar.
- Sözleşme (contract) değiştiğinde subject’leri versiyonlayın: mevcut
orders.created‘ı sessizce bozmak yerineorders.v2.createdkullanın. - Her servis/takım için ayrılmış bir isim alanı (namespace) ayırın, örn. sahibi olan servisle önekleyin:
billing.invoice.created.
5. Queue Group’lar
Queue group’lar yük dengelemeli (load-balanced) pub/sub sağlar — aynı queue group’taki birden fazla subscriber’dan yalnızca biri her mesajı alır (round-robin dağıtım), bu da worker’ların yatay ölçeklenmesini sağlar.
nc.QueueSubscribe("work.jobs", "workers", func(msg *nats.Msg) {
process(msg)
})
Bu worker’ın N adet örneğini aynı queue adıyla çalıştırın — NATS mesajları otomatik olarak aralarında dağıtır. Subject-tabanlı worker havuzları için wildcard’larla birleştirin:
nc.QueueSubscribe("orders.*.process", "order-workers", handler)
Desen: crash durumunda mesaj kaybını tolere edebiliyorsanız (yeniden teslimat yok) Core NATS ölçeklenen worker’lar için queue group kullanın; garanti işleme gerekiyorsa JetStream pull consumer kullanın.
6. Request-Reply
Core NATS yerleşik RPC semantiğine sahiptir: istemci benzersiz bir inbox subject’i üretir, reply-to ile publish eder ve yanıtı bekler.
// Yanıtlayan (Responder)
nc.Subscribe("math.add", func(msg *nats.Msg) {
result := add(msg.Data)
nc.Publish(msg.Reply, result)
})
// İstek yapan (Requester)
resp, err := nc.Request("math.add", []byte(`{"a":1,"b":2}`), 2*time.Second)
Arka planda bu, geçici bir inbox subject’i (_INBOX.<uuid>) kullanır — istemci kütüphanesi subscribe/unsubscribe işlemlerini şeffaf şekilde yönetir. Her zaman bir timeout belirleyin; yanıtlayanı olmayan bir istek zaten timeout’a kadar bekleyecektir, o yüzden bunu açık ve kısa tutun (DC-içi çağrılar için genellikle 1-5sn).
Scatter-Gather: bir istek publish edin, SubscribeSync + bir timer kullanarak belirli bir zaman penceresinde birden fazla yanıt toplayın; servis keşfi veya “tüm shard’lara sor” desenleri için kullanışlıdır.
7. JetStream’e Genel Bakış
JetStream bir kalıcılık ve akış (streaming) katmanı ekler. Temel yapı taşları:
- Stream: subject’lerden mesajları yakalayan kalıcı, sıralı, sadece-ekleme (append-only) günlük.
- Consumer: bir stream üzerindeki durumlu (stateful) imleç (push veya pull), onaylanan/onaylanmayan mesajları takip eder.
- Depolama arka uçları (storage backend):
file(disk, restart’lar arasında kalıcı) veyamemory(hızlı, geçici/volatile). - Teslimat garantileri: varsayılan olarak en az bir kez (at-least-once); mesaj tekilleştirme (
Nats-Msg-Idheader’ı) + idempotent consumer’lar ile tam olarak bir kez (exactly-once) elde edilebilir.
js, _ := nc.JetStream()
js.AddStream(&nats.StreamConfig{
Name: "ORDERS",
Subjects: []string{"orders.>"},
Storage: nats.FileStorage,
Retention: nats.LimitsPolicy,
MaxAge: 7 * 24 * time.Hour,
Replicas: 3,
})
js.Publish("orders.created", []byte(`{"id":123}`))
8. Stream’ler
Retention (Saklama) Politikaları
| Politika | Davranış |
|---|---|
LimitsPolicy | Limitlere (yaş/boyut/sayı) ulaşılana kadar mesajları tutar — klasik log retention’ı |
InterestPolicy | Bilinen tüm consumer’lar mesajı onayladığında (ack) mesajı siler — work queue için iyi |
WorkQueuePolicy | Mesaj, herhangi bir consumer onayladığı anda silinir (tek-consumer-semantiği garantisi: sadece bir consumer grubu bağlanabilir) |
Önemli Stream Yapılandırma Alanları
&nats.StreamConfig{
Name: "EVENTS",
Subjects: []string{"events.>"},
Retention: nats.LimitsPolicy,
MaxConsumers: -1,
MaxMsgs: 1_000_000,
MaxBytes: 10 * 1024 * 1024 * 1024, // 10GB
MaxAge: 30 * 24 * time.Hour,
MaxMsgSize: 1024 * 1024,
Storage: nats.FileStorage,
Replicas: 3, // Yüksek erişilebilirlik için RAFT ile çoğaltılır (replicate)
Discard: nats.DiscardOld,
Duplicates: 2 * time.Minute, // dedup penceresi
AllowRollup: true,
DenyDelete: true, // uyumluluk (compliance): manuel silme yok
DenyPurge: false,
}
Mesaj Tekilleştirme (Exactly-Once Publish)
js.Publish("orders.created", data, nats.MsgId("order-123-v1"))
Aynı Nats-Msg-Id ile bir mesaj Duplicates penceresi içinde tekrar gelirse, JetStream sessizce siler — bu, retry-güvenli producer’lar için ücretsiz idempotent publishing sağlar (temel önem taşır).
Stream Mirroring ve Sourcing
- Mirror: başka bir stream’in birebir kopyası (aynı subject’ler, aynı mesajlar) — bölgeler-arası okuma replikaları veya felaket kurtarma (DR) için harikadır.
- Source: birden fazla stream’den mesajları tek bir stream’de toplar, isteğe bağlı olarak subject yeniden eşlemesi (re-mapping) yapabilir — materyalize/toplu (aggregate) stream’ler oluşturmak için harikadır.
js.AddStream(&nats.StreamConfig{
Name: "ORDERS_EU_MIRROR",
Mirror: &nats.StreamSource{Name: "ORDERS_EU"},
})
9. Consumer’lar
Push ve Pull
- Push consumer: sunucu mesajları bir subscription subject’ine gönderir (push eder). Basittir ama backpressure’ı hassas şekilde kontrol etmek daha zordur — modern kullanımda büyük ölçüde pull ile yer değiştirmiştir.
- Pull consumer: istemci mesaj gruplarını (
Fetch) açıkça talep eder. Neredeyse tüm iş yükleri için önerilen varsayılan — eşzamanlılık, backpressure ve yatay ölçekleme üzerinde tam kontrol sağlar.
sub, _ := js.PullSubscribe("orders.>", "order-processor",
nats.ManualAck(),
nats.AckWait(30*time.Second),
nats.MaxDeliver(5),
)
for {
msgs, _ := sub.Fetch(10, nats.MaxWait(5*time.Second))
for _, m := range msgs {
if err := process(m); err != nil {
m.Nak() // olumsuz onay (negative ack): yeniden teslim et
continue
}
m.Ack()
}
}
Durable ve Ephemeral
- Durable (kalıcı) consumer (
Durableadı vardır): istemci bağlantısı kopsa bile hayatta kalır; kaldığı yerden devam eder. İmleç konumunu kaybetmemesi gereken her şey için kullanın. - Ephemeral (geçici) consumer (durable adı yok): son subscription kapandığında silinir — geçici/ad hoc sorgular veya hata ayıklama (debugging) için iyidir.
Ack (Onaylama) Stratejileri
| Mod | Anlamı |
|---|---|
AckExplicit | İstemci her mesajı ayrı ayrı Ack/Nak etmelidir (varsayılan, en güvenli) |
AckAll | N mesajını onaylamak, önceki onaylanmamış tüm mesajları da onaylar — daha yüksek verim, daha az granülerlik |
AckNone | Gönder-ve-unut, yeniden teslimat takibi yok |
Ack desenleri:
msg.Ack()— başarı.msg.Nak()— başarısızlık, yeniden teslim et (MaxDeliver‘a tabi).msg.NakWithDelay(d)— belirli bir gecikme sonrası yeniden teslim et.msg.Term()— zehirli mesaj (poison message), yeniden teslim etme, kalıcı olarak başarısız işaretle.msg.InProgress()— uzun süren işlemler için ack bekleme süresini uzat (heartbeat).
Temel Consumer Yapılandırması
&nats.ConsumerConfig{
Durable: "order-processor",
AckPolicy: nats.AckExplicitPolicy,
AckWait: 30 * time.Second,
MaxDeliver: 5,
MaxAckPending: 1000, // backpressure sınırı
DeliverPolicy: nats.DeliverAllPolicy, // ya da DeliverNewPolicy / DeliverByStartTimePolicy
ReplayPolicy: nats.ReplayInstantPolicy, // ya da ReplayOriginalPolicy (orijinal zamanlamaya sadık kal)
FilterSubject: "orders.created.*",
}
MaxAckPending, birincil backpressure kontrolünüzdür — aynı anda kaç mesajın beklemede (teslim edilmiş ama onaylanmamış) olabileceğini sınırlar, yavaş consumer’ların bunalmasını önler.
10. Key/Value Store
JetStream tabanlı KV store; yapılandırma (config), feature flag’ler, servis keşfi ve dağıtık kilitler (distributed lock) için idealdir.
kv, _ := js.CreateKeyValue(&nats.KeyValueConfig{
Bucket: "config",
History: 5,
TTL: 0, // süresiz
})
kv.Put("feature.dark_mode", []byte("true"))
entry, _ := kv.Get("feature.dark_mode")
// Değişiklikleri izle (gerçek zamanlı config yayılımı)
watcher, _ := kv.Watch("feature.>")
for update := range watcher.Updates() {
if update != nil {
fmt.Println(update.Key(), string(update.Value()))
}
}
- Arka planda KV, her anahtar için bir subject’e sahip
KV_<bucket>adlı basit bir stream’dir. History, her anahtar için kaç geçmiş revizyonun saklanacağını kontrol eder.- Dağıtık koordinasyon için önemli olan optimistic-concurrency-controlled yazımlar (compare-and-swap) için
kv.Update(key, val, revision)kullanın.
11. Object Store
Normal mesajlar için çok büyük olan blob’lar (dosyalar, resimler, yedekler) için — büyük objeleri bir stream üzerinden otomatik olarak parçalara (chunk) böler.
os, _ := js.CreateObjectStore(&nats.ObjectStoreConfig{Bucket: "uploads"})
os.PutFile("/local/path/report.pdf")
os.GetFile("report.pdf", "/tmp/report.pdf")
12. Güvenlik
Account’lar ve Çoklu Kiracılık (Multi-Tenancy)
Account’lar sert izolasyon (hard isolation) sağlar: her account kendi subject isim alanına (açıkça export/import edilmediği sürece hiçbir account’lar-arası sızıntı olmaz), kendi JetStream kaynak limitlerine ve bağımsız kimlik doğrulamasına sahiptir. Bu, NATS’in temel çoklu kiracılık yapı taşıdır — account’ları altyapıyı paylaşan ayrı organizasyonlar gibi düşünün.
Merkezi Olmayan Kimlik Doğrulama: NKeys ve JWT’ler
- NKeys: kimlik için kullanılan Ed25519 anahtar çiftleri (SSH anahtarları gibi) — hat üzerinden paylaşılan sır (secret) iletilmez.
- JWT’ler: bir account veya kullanıcının izinlerini (subject-seviyesinde pub/sub izin/red listeleri, limitler) tanımlar ve bir operator/account anahtarı tarafından imzalanır.
nsc add operator MyOperator
nsc add account MyApp
nsc add user --account MyApp MyUser
nsc edit user MyUser --allow-pub "orders.>" --allow-sub "orders.>,_INBOX.>"
TLS
Herhangi bir production veya çoklu host dağıtımında, istemciler ile sunucular arasında ve cluster/gateway route’ları arasında her zaman TLS etkinleştirin:
tls {
cert_file: "/etc/nats/server-cert.pem"
key_file: "/etc/nats/server-key.pem"
ca_file: "/etc/nats/ca.pem"
verify: true
}
Yetkilendirme (Authorization) için En İyi Uygulamalar
- En az yetki (least-privilege) subject izinleri kullanın — bir servise yalnızca ihtiyaç duyduğu tam subject desenlerinde
pub/subverin, production’da asla toptan>erişimi vermeyin. - Kiracıları (tenant) sadece subject önekleriyle değil, ayrı account’larla izole edin — önekler bir konvansiyondur, account’lar ise zorunlu kılınan (enforced) izolasyondur.
- Mümkün olduğunda kimlik bilgilerini kısa ömürlü JWT’ler ile döndürün (rotate); statik uzun ömürlü token’lardan kaçının.
- Geniş erişim açmak yerine account’lar-arası kasıtlı, açık iletişim için export/import kullanın.
13. Kümeleme ve Supercluster’lar
- Cluster: aynı bölge/veri merkezinde (DC) yüksek erişilebilirlik için birbirine bağlı 3+ NATS sunucusu (tek sayı, RAFT quorum). Genellikle 3 veya 5 node.
- Gateway: cluster’ları farklı bölgeler arasında bağlayarak bir supercluster oluşturur; yerellik-farkında (locality-aware) yönlendirme ile global subject görünürlüğü sağlar.
- Leaf node’lar: subject alanını tam mesh routing’e katılmadan merkezi bir cluster’a genişleten hafif edge bağlantıları (örn. IoT cihazları, şube ofisleri) — edge computing ve düşük bant genişliği bağlantıları için idealdir.
JetStream Replikasyonu
RAFT tabanlı replikasyon için stream’lerde Replicas: 3 ayarlayın — quorum sağlam olduğu sürece otomatik lider seçimi (leader election) ve veri kaybı olmadan 1 node arızasını tolere eder. JetStream cluster’larını quorum matematiğini korumak için her zaman tek sayıda replika (1, 3, 5) ile dağıtın.
Boyutlandırma (Sizing) Rehberi
- 3 node’lu cluster: 1 arızayı tolere eder.
- 5 node’lu cluster: 2 arızayı tolere eder, daha yüksek yazma gecikmesi (daha fazla replikasyon round trip’i) — yalnızca kritik stream’ler için kullanın.
- Aşırı replikasyon yapmayın: her stream’i “güvenlik için” 5 node’a replike etmek gereksiz yere verimi (throughput) düşürür.
14. İzleme ve Gözlemlenebilirlik
/varz,/connz,/subz,/routez,/jsz—nats-server -m 8222ile açığa çıkan HTTP izleme endpoint’leri.- NATS CLI:
nats stream info,nats consumer info,nats stream report, yük testi içinnats bench. - NATS Surveyor: filo-genelinde gözlemlenebilirlik için Prometheus exporter’ı + Grafana dashboard’ları.
- Server olayları: bağlantı/kopma/auth-ihlali olayları için
$SYS.>subject’lerine subscribe olun (sistem account erişimi gerektirir).
Uyarı (alert) kurulacak temel metrikler:
- Consumer
num_pending/num_ack_pendingbüyümesi (birikinti oluşumu). MaxAckPendingdoygunluğu (yavaş consumer’lar).MaxBytes‘a kıyasla JetStream depolama kullanımı.- Cluster RAFT lider seçim sıklığı (istikrarsızlık sinyali).
- Yavaş consumer bağlantı kesilmeleri (
nats.ErrSlowConsumer) — subscriber’ın yeterince hızlı tüketmediğini gösterir.
15. İstemci Kütüphaneleri
Resmi/birinci sınıf istemciler: Go (nats.go), Python (nats.py), Node.js (nats.js), Java, C#/.NET, Rust, Ruby, Elixir (gnat), C.
Genel istemci en iyi uygulamaları:
- Süreç (process) başına tek bir bağlantı yeniden kullanın; birçok bağlantı açmak yerine subject’leri onun üzerinden çoğullayın (multiplex).
ErrSlowConsumer‘ı her zaman ele alın — bu, callback’inizin yetişemediği ve mesajların bekleyen (pending) tampondan düşürüldüğü anlamına gelir.- Yüksek hacimli subject’leri işleyen subscription’lar için açık
PendingLimitsbelirleyin. - Yüksek verimli producer’lar için tamamlanma callback’i ile async publishing (
js.PublishAsync) kullanın, ama kapanmadan öncejs.PublishAsyncComplete()ile takip edin/flush edin.
16. Tasarım Desenleri (Patterns)
Olay Odaklı Mikroservisler (Event-Driven Microservices)
Servisler domain olaylarını iyi isimlendirilmiş subject’lerde (<service>.<entity>.<event>) publish eder; diğer servisler ilgilendikleri olaylara wildcard’lar üzerinden subscribe olur. Producer’ları consumer’lardan tamamen ayrıştırır (decouple).
CQRS + Event Sourcing
LimitsPolicy stream’ini kalıcı olay günlüğü (source of truth) olarak kullanın; stream’i durable bir consumer ile tüketip olayları bir veritabanına/cache’e uygulayarak okuma-modeli (read-model) projeksiyonları oluşturun. Projeksiyonları sıfırdan yeniden inşa etmek için stream’i tekrar oynatın (DeliverPolicy: DeliverAllPolicy).
Work Queue (İş Kuyruğu)
Worker örnekleri arasında paylaşılan bir durable adla WorkQueuePolicy stream’leri + pull consumer’lar kullanın — NATS her mesajın gruptaki tam olarak bir worker’a gitmesini garanti eder, onaylandığında silinir.
Saga / Choreography (Koreografi)
Servisleri olaylar üzerinden zincirleyin: Servis A order.created publish eder → Servis B (queue-group subscriber) ödemeyi işler, payment.completed veya payment.failed publish eder → Servis C buna göre tepki verir. Tüm saga’yı izlemek için header’larda korelasyon ID’leri (correlation ID) kullanın.
Request-Reply Mikroservisleri (Senkron RPC)
Kalıcılığa ihtiyaç duymadığınız düşük gecikmeli dahili RPC çağrıları (kimlik doğrulama kontrolleri, aramalar) için Core NATS request-reply kullanın — cluster içindeki servisler-arası çağrılar için HTTP’den çok daha basit ve hızlıdır.
Fan-Out / Fan-In
Fan-out: bir publisher, birçok bağımsız subscriber (her biri bir kopya alır), düz subscribe ile (queue group değil). Fan-in: bir subject’e birçok publisher, tek bir consumer toplar (aggregate) — telemetri/loglama pipeline’larında yaygındır.
Outbox Pattern
Publishing’in bir DB yazımıyla transaction olarak tutarlı olması gerektiğinde, olayı aynı DB transaction’ı içinde bir “outbox” tablosuna yazın, ardından ayrı bir relay süreci, dedup-güvenli, exactly-once-etkili teslimat için DB satır ID’sini Nats-Msg-Id olarak kullanarak JetStream’e publish eder.
KV Üzerinden Dağıtık Kilitler
Hafif dağıtık kilitleme için compare-and-swap primitifi olarak kv.Create() (anahtar zaten varsa başarısız olur) kullanın; anahtarı silerek veya TTL‘in dolmasına izin vererek serbest bırakın.
17. En İyi Uygulamalar Kontrol Listesi
- Varsayılan olarak pull consumer kullanın; push consumer’ları eski/basit durumlar için ayırın.
-
MaxDeliverbelirleyin ve tükenen mesajları manuel olarak bir dead-letter subject‘ine yönlendirin (JetStream’de yerleşik DLQ yoktur — bunuTerm()+ izleyen bir consumer ile ya da N başarısızlıktan sonra bir*.dlqsubject’ine kopya publish ederek kendiniz kurun). -
MaxAckPending‘i her zaman bilinçli olarak ayarlayın, varsayılanlara güvenmeyin. - Retry’ların mümkün olduğu her yerde idempotent publishing için
Nats-Msg-Idkullanın. - Uzun ömürlü herhangi bir şey için durable consumer isimleri kullanın; ephemeral’ı sadece debug/ad hoc için ayırın.
- Subject’leri hiyerarşik tasarlayın; servis başına subject taksonominizi belgelendirin.
- Kiracı izolasyonu için sadece subject önekleri değil, account’lar kullanın.
- Production’da her yerde TLS’i etkinleştirin.
- Yüksek erişilebilirlik gereken JetStream stream’leri için tek sayıda replika sayısı (3 veya 5) kullanın.
-
num_ack_pending,num_pendingve slow-consumer olaylarını izleyin. - Async publish kullanırken süreç kapanmadan önce
js.PublishAsyncComplete()çağırın. - Şema değişikliğinde consumer’ları sessizce bozmak yerine subject’leri versiyonlayın (
v2.). -
MaxAge/MaxBytes/MaxMsgsretention’ını doğru boyutlandırın — stream’lerin varsayılan olarak sınırsız büyümesine izin vermeyin.
18. Sık Yapılan Hatalar
- Kaybedilmemesi gereken herhangi bir şey için Core NATS kullanmak. Subscriber yoksa mesaj gider. Kalıcılık-kritik veri için her zaman JetStream kullanın.
- Ack etmeyi unutmak. Onaylanmamış (unacked) mesajlar sonsuza kadar yeniden teslim edilir (
MaxDeliver‘a kadar), consumer’lar idempotency’yi ele almazsa yinelenen işleme döngülerine yol açar. - Kolaylık olsun diye ACL’lerde
>kullanmak. Bu bir güvenlik anti-pattern’idir; subject’leri her zaman sıkı şekilde sınırlayın. MaxAckPendingayarlamamak. Sınırsız bekleyen mesajlar yavaş consumer’ları bunaltıp belleği patlatabilir.- Bağlantı string’inde tek seed URL. Yeniden bağlanma denemesi sırasında o tek node çökmüşse, istemci cluster’ın geri kalanını keşfedemez.
- Slow consumer hatalarını görmezden gelmek. Bunlar istemci-tarafı bekleyen tampondan mesajları sessizce düşürür — uyarı değil, kritik alarm olarak ele alın.
- Stream’leri aşırı replike etmek. Kritik olmayan veride gereksiz R5 replikasyonu, hiçbir fayda sağlamadan yazma gecikmesini artırır.
- Failover’ı test etmemek. Uygulamanızın yeniden bağlanma/yeniden seçimi (re-election) düzgün şekilde ele aldığını doğrulamak için staging ortamında düzenli olarak bir JetStream lider node’unu öldürün.
- “Queue group” (Core NATS yük dengeleme) ile “work queue stream” (JetStream kalıcı iş dağıtımı) kavramlarını karıştırmak — benzer sorunları çözerler ama çok farklı kalıcılık garantilerine sahiptirler.
19. Dağıtım (Deployment) Örnekleri
Minimal Tek Node (geliştirme)
nats-server -js -sd /data/jetstream
3 Node’lu Cluster (docker-compose taslağı)
services:
nats1:
image: nats:2.10-alpine
command: "-js -sd /data -cluster_name NATS -cluster nats://0.0.0.0:6222 -routes nats://nats2:6222,nats://nats3:6222"
nats2:
image: nats:2.10-alpine
command: "-js -sd /data -cluster_name NATS -cluster nats://0.0.0.0:6222 -routes nats://nats1:6222,nats://nats3:6222"
nats3:
image: nats:2.10-alpine
command: "-js -sd /data -cluster_name NATS -cluster nats://0.0.0.0:6222 -routes nats://nats1:6222,nats://nats2:6222"
Kubernetes
Stream/Consumer’ları deklaratif olarak CRD’ler şeklinde yönetmek için resmi NATS Helm chart‘ını veya NACK (NATS için Kubernetes controller) kullanın:
apiVersion: jetstream.nats.io/v1beta2
kind: Stream
metadata:
name: orders
spec:
name: ORDERS
subjects: ["orders.>"]
storage: file
replicas: 3
maxAge: "168h"
20. Kaynaklar
- Resmi dokümantasyon: https://docs.nats.io
- NATS by Example (çalıştırılabilir kod örnekleri): https://natsbyexample.com
- GitHub: https://github.com/nats-io
- Topluluk Slack’i: docs.nats.io üzerinden
natsCLI:brew install nats-io/nats-tools/natsya da GitHub release’leri üzerinden
Bu rehber NATS Server 2.10+ semantiğini yansıtır. API değişiklikleri için her zaman en güncel resmi dokümantasyonla karşılaştırın.