SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor

Tek Bir VPS İçin En İyi Mesaj Kuyruğu Seçimi

Tek bir sunucuda NATS, RabbitMQ ve Kafka arasındaki farkları inceleyin. Teslimat garantileri, disk kullanımı ve Postgres tabanlı kuyrukların neden daha verimli olduğunu öğrenin.

Tek bir sunucu için kısa cevap

Tek bir VPS üzerinde mesaj kuyruğu kullanmak, hızdan ziyade teslimat garantileriyle ilgili bir karardır. Tek bir makinede darboğaz genellikle broker değildir; uygulama kodunuz, veritabanınız ve diskiniz çok daha önce darboğaz haline gelir. Hata davranışını kabul edebileceğiniz aracı seçin ve ardından elinizdeki makineyi ölçümleyin.

Okuyucuların çoğunun değerlendirmesi gereken sırayla dört seçenek şunlardır:

  • Halihazırda kullandığınız veritabanını kullanın. SELECT ... FOR UPDATE SKIP LOCKED ile Postgres çalışan bir iş kuyruğudur ve izlenmesi gereken yeni bir süreç eklemez.
  • Her mesajın onaylanması, sınırlı sayıda yeniden denenmesi ve ardından bir insanın inceleyebileceği bir yere park edilmesi gereken iş birimleri olduğu durumlarda RabbitMQ kullanın.
  • Mesajlar, sisteminizin çeşitli bölümlerinin tepki verdiği olaylar olduğunda NATS kullanın. Yeniden başlatma sonrasında varlığını sürdürmesi gereken olaylar için JetStream özelliğini açın.
  • Bir alt sistem yalnızca Kafka protokolü ile iletişim kurabiliyorsa Kafka kullanın. Tek bir sunucuda bu, geriye kalan tek geçerli nedene yakındır.

Bu kılavuzun geri kalanı gerekçeleri açıklamaktadır: her seçeneğin küçük bir VPS üzerinde bellek ve disk maliyeti nedir, makine yeniden başlatıldığında ne olur ve kullanıcılarınız hissetmeden önce birikmiş iş yükünü görmenizi sağlayan kesin komut nedir.

Teslimat garantisinin gerçek anlamı

At most once (en fazla bir kez), mesaj aracısının mesajı iletip unutması anlamına gelir. Eğer hiçbir tüketici bağlı değilse veya bir tüketici işlem sırasında hata verirse, mesaj kaybolur ve bu durum raporlanmaz.

At least once (en az bir kez), tüketicinin işlem başarıyla tamamlandıktan sonra bir onay (ack) göndermesi anlamına gelir. Bu onay ulaşana kadar aracı mesajı tutar ve tekrar iletir. Yeniden iletim nedeniyle işleyicilerinizin idempotent (eşgüçlü) olması gerekir: aynı mesajın iki kez işlenmesi, kredi kartından iki kez çekim yapılmasına neden olmamalıdır. Uçtan uca "tam olarak bir kez" teslimat, bir mesaj aracısının size sunduğu bir özellik değildir. Bu, kendi veritabanınızdaki benzersiz bir anahtar ile sağlanır.

Replay (tekrar oynatma) ayrı bir özelliktir. Bir kuyruk, mesaj onaylandığı anda onu siler. Bir günlük (log) ise mesajı belirli bir saklama süresi boyunca tutar; böylece yeni bir tüketici en baştan başlayarak tüm geçmişi okuyabilir. Kafka ve NATS JetStream birer günlük yapısıdır. RabbitMQ ise bir kuyruktur. Bu fark, mimarileri verimlilikten (throughput) daha fazla şekillendirir.

Dead lettering (ölü mesaj kuyruğu), sürekli hata veren mesajların başına gelen durumdur. Bu mekanizma olmazsa, hatalı bir mesaj sonsuz döngüye girer ve bu döngü, sistemin bozuk olduğunu değil, yoğun çalıştığını düşündürebilir.

Postgres ile başlayın ve broker'ın yetkinliğini kanıtlayın

Çoğu tek uygulamalı iş yükü, günde birkaç bin arka plan işinden oluşur. Bu miktar bir tabloya sığar.

CREATE TABLE job (
  id        bigserial   PRIMARY KEY,
  payload   jsonb       NOT NULL,
  run_after timestamptz NOT NULL DEFAULT now(),
  attempts  int         NOT NULL DEFAULT 0
);
CREATE INDEX job_ready_idx ON job (run_after, id);

Bir worker, bir işlem (transaction) içerisinde tek bir işi sahiplenir.

BEGIN;
SELECT id, payload
  FROM job
 WHERE run_after <= now()
 ORDER BY id
   FOR UPDATE SKIP LOCKED
 LIMIT 1;
-- run the work, then remove the row
DELETE FROM job WHERE id = $1;
COMMIT;

FOR UPDATE SKIP LOCKED tüm işin püf noktasıdır. Döndürdüğü satırı kilitler ve başka bir işlemin halihazırda kilitlediği satırları atlar; böylece iki worker asla aynı işi sahiplenmez. Bir worker çökerse, Postgres işlemini iptal eder, kilit serbest kalır ve satır bir sonraki worker için görünür hale gelir. En az bir kez teslimat (at-least-once delivery), attempts değerini artırarak yeniden deneme ve bir ölü mektup (dead letter) tablosuna sahip olursunuz; üstelik tüm bunları zaten ödemesini yaptığınız dayanıklılık özellikleri ile elde edersiniz. Birikmiş işleri sorgulamak tek bir sorgu ile mümkündür: SELECT count(*) FROM job WHERE run_after <= now();

Sistemin tıkandığı nokta burasıdır. Her sahiplenme ve silme işlemi bir yazma işlemidir; bu nedenle yüksek iş hacmi, geride ölü satır sürümleri bırakır. Kuyruk tablosu, autovacuum sürecinin hızına yetişemediği şişme (bloat) sorununun klasik örneğidir. Uzun süren işler durumu daha da kötüleştirir; çünkü iş süresince açık tutulan bir işlem, tüm veritabanı için vacuum ufuk çizgisini (vacuum horizon) geride tutar. Polling (sorgulama) gecikmeye neden olur; LISTEN ve NOTIFY kullanımı polling işlemini ortadan kaldırsa da yazma yükünü azaltmaz. İş tablosu en yoğun tablonuz haline geldiğinde veya ikinci bir servisin aynı olaylara ihtiyacı olduğunda, iş yükünü dışarı taşıyın. Bu seçim, veritabanının nasıl dağıtıldığı ile doğrudan ilişkilidir; bu nedenle yanına bir broker eklemeden önce veritabanının Docker içinde mi yoksa ana makinede mi çalışacağına karar verin.

Redis, halihazırda çalıştırıyor olabileceğiniz diğer seçenektir. Redis Streams, XADD ve XREADGROUP ile tüketici grupları, grup başına bekleyenler listesi ve ölen bir tüketiciden işi geri almak için XAUTOCLAIM özelliklerini sunar. Küçük ve hızlıdır. Tek bir sunucudaki dürüst gerçek şudur: yaygın appendfsync everysec ayarı ile bir elektrik kesintisi yaklaşık bir saniyelik yazma kaybına neden olabilir. Bu durum önbellek geçersiz kılma işlemleri için kabul edilebilir olsa da ödemeler için yanlıştır. Uygulamanız üretim ortamında VPS üzerinde SQLite etrafında inşa edilmiş tek bir süreç ise, aynı sahiplen-ve-sil modeli çalışır; ancak SQLite'ın SKIP LOCKED karşılığı yoktur ve her worker tek bir yazma kilidi üzerinde serileşir.

NATS core: bellek kullanmayan konu yönlendirmesi

docker run -d --name nats \
  -p 4222:4222 -p 127.0.0.1:8222:8222 \
  nats:2.14 -m 8222

Ağustos 2026 itibarıyla güncel sunucu sürümü 2.14'tür. -m 8222 parametresi, varsayılan olarak kapalı olan ve kimlik doğrulaması içermeyen HTTP izleme portunu etkinleştirir; bu nedenle yukarıda belirtildiği gibi localhost adresine bağlayın.

Core NATS "en fazla bir kez" (at most once) teslimat prensibiyle çalışır ve hiçbir veri depolamaz. Bir yayıncı, orders.created gibi bir konuya mesaj gönderdiğinde, filtreleri eşleşen tüm aboneler mesajın bir kopyasını alır. Eğer abone olan kimse yoksa mesaj düşürülür ve yayıncı herhangi bir hata almaz; çünkü yayıncının görevi, sunucu baytları kabul ettiği anda sona ermiştir. Bir kuyruk grubu (aynı grup adını paylaşan birden fazla abone), sunucunun her mesaj için gruptan bir üyeyi seçmesini sağlar; bu, kuyruk depolaması yapmadan iş yükünün paylaşılmasına olanak tanır.

Sistem ayak izi, abonelik durumu ve her bağlantı için ayrılan yazma arabelleğinden oluşur; bu nedenle mesaj hacminden ziyade bağlantı sayısını takip eder ve disk üzerinde hiçbir veri birikmez. Yeniden başlatma davranışı da bu yapıdan kaynaklanır: iletim halindeki mesajlar kaybolur, istemciler kendi başlarına yeniden bağlanır ve beklenmesi gereken bir kurtarma adımı yoktur.

İzlenecek bir birikim (backlog) olmadığı için kayıpları izleyin. Bir abone, soketini sunucunun yazma hızından daha yavaş okuduğunda, sunucunun o istemci için ayırdığı arabellek dolar. İstemci, yazma süresi dolana kadar arayı kapatamazsa, sunucu bağlantıyı tamamen kapatır ve bir sayacı artırır.

curl -s http://localhost:8222/varz | jq '.slow_consumers, .connections, .in_msgs, .out_msgs'

Sürekli artan bir slow_consumers değeri, mesajların düşürüldüğü anlamına gelir; bu nedenle değeri tek seferlik okumak yerine bu durum için uyarı mekanizması kurun. Core NATS, değeri hızla geçerliliğini yitiren mesajlar için uygundur: bir metrik, bir durum güncellemesi veya bir sonraki olayın zaten geçersiz kılacağı bir önbellek temizleme işlemi gibi.

NATS JetStream: aynı süreçte kalıcı akışlar ve tekrar oynatma

JetStream ikinci bir ürün değildir. Aynı ikili dosya içerisinde yer alan ve tek bir bayrak ile etkinleştirilen bir alt sistemdir.

docker run -d --name nats \
  -p 4222:4222 -p 127.0.0.1:8222:8222 \
  -v nats-data:/data \
  nats:2.14 -js -sd /data -m 8222

-sd /data, veri deposu dizinini belirler. Bu ayarı boş bırakırsanız JetStream verilerini /tmp altında saklar; bu dizin ismi kadar kalıcıdır. nats-box imajı içerisinde gelen CLI aracını kullanarak bir akış oluşturun.

docker run --rm -it --network host natsio/nats-box:latest \
  nats stream add ORDERS \
    --subjects 'orders.>' \
    --storage file \
    --retention limits \
    --max-age 72h \
    --max-bytes=1073741824 \
    --discard old \
    --defaults

Buradaki her limit, küçük bir sunucuda yerini hak eder. --storage file, bir çökme sonrasında hayatta kalan veridir; bellek içi akışlar çökme sonrası kaybolur. --max-bytes=1073741824, akışı bayt cinsinden 1 GiB ile sınırlar ve --discard old, bu sınıra ulaşıldığında yeni yazma işlemlerini reddetmek yerine en eski mesajları siler. Sınırı belirlemezseniz, kontrolden çıkan bir yayıncı diski doldurur; bu noktada veritabanınız da durur çünkü aynı diski paylaşırlar.

Kalıcı bir tüketici, akış içerisindeki kendi konumunu korur ve yeniden başlatma sonrasında bu konumu hatırlar. Tüketici üzerinde --max-deliver ayarını yapın; böylece sürekli başarısız olan bir mesajın sonsuza kadar tekrar iletilmesi engellenir. Bir mesajın iletim hakkı tükendiğinde, JetStream $JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.> üzerinde bir bildirim yayınlar. Bu konuya abone olmak, RabbitMQ'nun bir özellik olarak sunduğu "dead letter" (ölü mesaj) yolunu oluşturmanızı sağlar. Bu, bizzat yazmanız gereken gerçek bir iş yüküdür.

Birikmiş mesajları görmek için depolanan mesaj sayılarını nats stream report ile, tüketici başına bekleyen onayları ve işlenmemiş mesajları ise nats consumer report ORDERS ile görüntüleyin. Alarm kurmanız gereken değer, işlenmemiş mesaj sayısıdır. Disk maliyeti, depolama dizini üzerinde du -sh çalıştırılarak görülebilir; bu alan bir saklama limiti ile temizlenene kadar büyümeye devam eder.

RabbitMQ: her mesajı onaylayın, hataları ayırın

docker run -d --name rabbitmq \
  -p 5672:5672 -p 127.0.0.1:15672:15672 \
  -v rabbitmq-data:/var/lib/rabbitmq \
  rabbitmq:4-management

Ağustos 2026 itibarıyla güncel seri 4.3 sürümüdür. 5672 numaralı port AMQP (gelişmiş mesaj kuyruklama protokolü), 15672 numaralı port ise yönetim arayüzüdür. Arayüzü localhost üzerinde tutun ve SSH tüneli aracılığıyla erişin.

Kuyrukları x-queue-type argümanı quorum olarak ayarlanmış şekilde tanımlayın; varsayılan değer hala classic şeklindedir. Quorum kuyrukları her zaman kalıcıdır ve başka bir işlem yapmadan önce veriyi diske yazar; bu sayede tek bir düğümde, kalıcı ve geçici seçenekler matrisi yerine tek ve net bir davranış elde edersiniz. Ölü mektup (dead letter) hedefini bir politika ile belirleyin.

docker exec rabbitmq rabbitmqctl set_policy DLX ".*" \
  '{"dead-letter-exchange":"my-dlx", "dead-letter-routing-key":"my-routing-key"}' \
  --apply-to queues --priority 7

Bir mesaj dört nedenden dolayı ölü mektup olarak işaretlenir: bir tüketici onu basic.reject veya basic.nack ile reddeder ve requeue değeri false olarak ayarlanmıştır, mesajın TTL (yaşam süresi) değeri dolar, kuyruk bir uzunluk sınırını aşar veya quorum kuyruğu teslimat sınırını geçer. Bu sınır RabbitMQ 4.0 ve sonrasında varsayılan olarak 20'dir; bu nedenle hata fırlatan ve nack gönderen bir işleyici, döngüye girmek yerine mesajı yirmi kez yeniden dener ve ardından ölü mektup değişimine (dead letter exchange) iletir.

Bellek, RabbitMQ'nun küçük bir VPS üzerinde insanları şaşırttığı noktadır. Varsayılan yüksek su işareti (high watermark) mevcut RAM'in 0.6'sıdır ve düğüm bu sınırı aştığında RabbitMQ yayın yapan tüm bağlantıları engeller. Uygulamanız bir hata almaz. Yayınlama işlemi asla geri dönmez, bu da kendi kodunuzda bir donma gibi görünür. Başlangıç günlüğü, düğümün hesapladığı değeri yazdırır:

Memory high watermark set to 1024 MiB (1073741824 bytes) of 8192 MiB (8589934592 bytes) total

Disk alarmı, boş alan varsayılan olarak 50 MB'ın altına düştüğünde yayıncıları aynı şekilde engeller. Quorum kuyrukları kendi aritmetiklerini de buna ekler: dokümantasyon, mesaj başına en az 32 bayt bellek içi meta veri (her 30.000 mesaj için yaklaşık 1 MB) bütçeler ve RAM'de etkili yazma öncesi günlük (write-ahead log) boyutunun en az üç katını önerir. WAL sınırı varsayılan olarak 512 MiB'dir, bu nedenle sadece bu öneri bile 1.5 GB RAM gerektirir. 2 GB'lık bir sunucuda, varsayılan değerin uyacağını ummak yerine rabbitmq.conf dosyasında bu değeri düşürün.

raft.wal_max_size_bytes = 64000000
vm_memory_high_watermark.relative = 0.5

Birikmiş iş (backlog) iki sayıdan oluşur ve bu çift, hangi hatayla karşılaştığınızı size söyler.

docker exec rabbitmq rabbitmqctl list_queues name messages messages_ready messages_unacknowledged

messages_ready bir tüketiciyi beklemektedir. messages_unacknowledged teslim edilmiş ancak asla onaylanmamıştır (acked). Sabit bir hazır mesaj sayısının yanında yükselen bir onaylanmamış mesaj sayısı, çalışanlarınızın işleri aldığını ancak tamamlamayı bıraktığını gösterir; bu, sadece geride kalmış bir kuyruktan farklı bir hatadır.

Tek sunucuda Kafka ve mantıklı olduğu sınır

KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format --standalone -t $KAFKA_CLUSTER_ID -c config/server.properties
bin/kafka-server-start.sh config/server.properties

Bu, Kafka 4.0 ile ZooKeeper'ın yerini alan yerleşik denetleyici KRaft (Kafka Raft) modunda çalışan ve Ağustos 2026 itibarıyla güncel olan Kafka 4.3.1 hızlı başlangıç kılavuzudur. Konteyner karşılığı apache/kafka:4.3.1 şeklindedir.

Başlatma betiği, siz ayarlamadığınız sürece export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G" değerini atar; bu nedenle broker, tek bir mesaj depolamadan önce 1 GB Java yığını (heap) ayırır ve okuma yaptığı sayfa önbelleği (page cache) için bunun üzerinde boş RAM bekler. 2 GB RAM'e sahip bir VPS üzerinde uygulamanız, kalan kaynaklar için JVM ile rekabet eder.

Veri tutma süresi (retention) bir sonraki sürprizdir. log.retention.hours varsayılan olarak 168, yani yedi gündür; log.retention.bytes ise varsayılan olarak -1'dir, yani herhangi bir boyut sınırı yoktur. Kafka, mesajları her tüketici okumuş olsa da olmasa da bu süre boyunca tutar. Bu, Kafka'yı tercih etme sebebinizdir ancak küçük bir diskte aynı zamanda hata modudur; bu yüzden bir sorunla karşılaşmadan önce konu (topic) başına bir bayt sınırı belirleyin.

Şimdi dürüst olma vakti. Tek bir broker, 1 kopyalama faktörü (replication factor) anlamına gelir; dolayısıyla acks=all tek bir diskteki tek bir fsync işlemine karşılık gelir. Bir JVM broker ve bir denetleyicinin işletim maliyetiyle, tek bir makinenin dayanıklılığına sahip olursunuz. Bölümler (partitions), sahip olmadığınız broker'lar arasında paralellik sağlar. Kopyalama, raf farkındalığı (rack awareness) ve diğer filo özellikleri atıl kalır. JetStream, aynı kutuda çok daha az bellek kullanımıyla aynı dayanıklı tekrar oynatma (replay) özelliğini sunar. Burada Kafka kullanmayı haklı çıkaran iki neden vardır: aşağı yönlü bir aracın yalnızca Kafka protokolüyle konuşması (Debezium ile veri değişimi yakalama veya analitik yükleyici) ya da üretim topolojisini minyatür ölçekte yeniden oluşturuyor olmanız. Bir kümeye (cluster) dönüşmeyi planlamak, daha fazla makine satın almayı planlamaktır; o zamana kadar yapılan takas, tek düğümde k3s çalıştırmak ile aynıdır; tek bir düğümün güvenilirliği için küme karmaşıklığını göze alırsınız.

Kafka'daki birikmiş iş (backlog), tüketici gecikmesidir (consumer lag).

bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group my-group

Her bölüm için LOG-END-OFFSET eksi CURRENT-OFFSET değerine karşılık gelen LAG sütununu okuyun. Diğerleri sabit kalırken bir bölümdeki gecikmenin artması, dengesiz bir anahtara (key) işaret eder; çünkü aynı anahtara sahip tüm mesajlar aynı bölüme düşer ve tek bir tüketici bunları tek başına işler.

Sunucu yeniden başlatıldığında ne olur

Core NATS, uçuş halindeki tüm verileri kaybeder ve kurtarılacak bir şey olmadığı için anında geri gelir. JetStream, stream'leri ve consumer konumlarını store dizininden yeniden yükler; böylece consumer'lar kaldıkları offset değerinden devam eder. RabbitMQ, quorum kuyruklarını diskten kurtarır; ancak klasik geçici kuyruklar ve persistent delivery modu olmadan yayınlanan tüm mesajlar silinir. Kafka, başlangıçta log segmentlerini tekrar oynatır; temiz olmayan bir kapatma sonrasında bu kurtarma taraması, broker bağlantı kabul etmeden önce küçük bir diskte bile dakikalar sürebilir.

İki ayarın bir kez yapılması faydalıdır. Konteynera bir restart policy (restart: unless-stopped) atayın veya systemd unit dosyasını etkinleştirin; böylece broker, çekirdek yükseltmesi sonrası gerçekleşen yeniden başlatmalardan sonra sizin müdahalenize gerek kalmadan ayağa kalkar. Ardından sıralamayı yönetin: uygulamanızdan yirmi saniye sonra hazır hale gelen bir broker, ilk bağlantıları reddedecektir ve bazı istemci kütüphaneleri yeniden denemek yerine sonlanmayı tercih eder. Uygulamanızı, broker hazır olana kadar bağımlı servisi bekleten Compose sağlık kontrolleri ile broker'a bağlayın.

Kendi VPS'nizdeki maliyet: Tahmin yerine ölçüm

Yayınlanan iş hacmi (throughput) verileri, sizin sahip olmadığınız donanımlar üzerinde, genellikle yerel NVMe diskli çok çekirdekli sunucularda ölçülür. Bu verileri bir üst sınır olarak kabul edin ve kendi sunucunuzu ölçün.

docker stats --no-stream
free -m
sudo du -sh /var/lib/docker/volumes/*/_data

Bu testleri önce broker boşta iken, ardından gerçek trafiğiniz altında çalıştırın. İki değer arasındaki fark, broker'ın uygulamanızın yanında çalışıp çalışmayacağına karar vermenizi sağlar. Kaba bir iş hacmi tabanı belirlemek için başkalarının blog yazılarını değil, her projenin kendi yük oluşturucusunu kullanın: NATS için nats bench pub test --msgs 100000 --clients 2, Kafka için bin/kafka-producer-perf-test.sh ve RabbitMQ için PerfTest. Yük oluşturucuyu aynı VPS üzerinde çalıştırmak, broker ve oluşturucuyu birlikte ölçer; bu durumu raporunuzda belirttiğiniz sürece bir sakıncası yoktur.

Tümü için geçerli olan tek bir üst sınır vardır. Buradaki her kalıcı (durable) seçenek fsync işlemini bekler; bu nedenle ağ tabanlı depolama (network-attached storage) kullanan bir VPS'de sınırı disk belirler ve broker değiştirmek bu sınırı değiştirmez.

Üç iş yükü ve her birinin ihtiyaç duyduğu mesaj kuyruğu

  1. E-posta gönderme, görsel boyutlandırma veya webhook iletme gibi bir web uygulaması için arka plan işleri. Postgres ve SKIP LOCKED ile başlayın. Mesaj bazlı onay mekanizmasına, teslimat sınırına ve kendi mantığınızı yazmadan inceleyebileceğiniz bir ölü mektup kuyruğuna (dead letter queue) ihtiyaç duyduğunuzda veya iş tablosu veritabanındaki en yoğun tablo haline geldiğinde RabbitMQ ve quorum kuyruklarına geçiş yapın.
  2. Kaybolan bir mesajın daha yenisiyle hızla değiştirildiği, birden fazla dahili servisin tepki verdiği olaylar. Yönlendirme şeması olarak konu başlıklarını (subjects) ve iş paylaşımı gereken yerlerde kuyruk gruplarını (queue groups) kullanan Core NATS tercih edin. Yeniden başlatma sonrasında hayatta kalması gereken dar kapsamlı konu başlıkları için bir JetStream akışı ekleyin, geri kalanını bellekte tutun.
  3. Denetim izi oluşturmak, okuma modelini yeniden inşa etmek veya daha sonra analitik verisi sağlamak amacıyla tüketicilerin en baştan okuduğu bir olay günlüğü. Dosya depolama ve açık bayt sınırı ile JetStream kullanın. Yalnızca bir alt sistem Kafka protokolünü zorunlu kılıyorsa Kafka'yı seçin ve bu uyumluluğun bedeli olarak JVM heap kullanımını kabul edin.

Tek bir sunucuda yanlış seçimin maliyeti throughput değildir. Mesajların hala var olup olmadığını bilmeniz gereken, sabahın üçündeki kurtarma sürecidir. Seçiminizi buna göre yapın.

FAQ

Kafka'yi 2 GB RAM'e sahip bir VPS üzerinde çalıştırabilir miyim?

Çalışır ancak kaynaklar oldukça kısıtlı olacaktır. Siz aksi bir ayar yapmadığınız sürece bin/kafka-server-start.sh, KAFKA_HEAP_OPTS="-Xmx1G -Xms1G" değerini atar; bu da JVM'in herhangi bir mesaj depolamadan önce 1 GB RAM talep etmesi demektir. Kafka, bunun üzerindeki boş belleği sayfa önbelleği (page cache) olarak kullanır. Aynı sunucuda uygulamanızı ve veritabanınızı da çalıştırırsanız sistem swap kullanmaya başlar. Ayrıca bu durumda replikasyon faktörü 1 olur; yani acks=all tek bir disk üzerinde tek bir fsync işlemine karşılık gelir. Bu da Kafka'nın işletim maliyetini öderken, sunduğu dayanıklılık modelinden faydalanamamanız anlamına gelir. NATS JetStream, aynı donanım üzerinde çok daha az bellek tüketimiyle dayanıklı mesaj tekrarı (durable replay) sağlar.

Zaten Postgres kullanıyorsam bir mesaj kuyruğuna ihtiyacım var mı?

Çoğu zaman hayır. Bir job tablosunu bir işlem (transaction) içerisinde SELECT ... FOR UPDATE SKIP LOCKED ile okumak; en az bir kez teslimat (at-least-once delivery), güvenli eşzamanlı çalışanlar, yeniden denemeler ve ölü mektup tablosu (dead letter table) sağlar. Üstelik izlenmesi gereken ek bir servis yoktur ve yedekleme süreçleriniz zaten hazırdır. Mesaj kuyruğuna geçişi gerektiren durumlar bellidir: kuyruk tablosu en ağır yazma yükünüz haline gelip autovacuum işlemini yavaşlatıyorsa, uzun süren işler işlemleri açık tutarak veritabanının tamamında vacuum işlemini engelliyorsa veya ikinci bir servisin aynı olayları bağımsız olarak tüketmesi gerekiyorsa mesaj kuyruğuna geçilmelidir.

Arka plan işleri için NATS JetStream mi yoksa RabbitMQ mu kullanmalıyım?

Mesaj bazlı onay mekanizması, teslimat sınırı ve ölü mektup yönlendirmesi (dead letter routing) gibi özellikleri yerleşik olarak istiyorsanız RabbitMQ tercih edin. Quorum kuyrukları her zaman dayanıklıdır; RabbitMQ 4.0 sürümünden itibaren teslimat sınırı varsayılan olarak 20'dir ve bir politika sayesinde, başarısız olan mesajlar inceleyebileceğiniz bir ölü mektup değişimine (dead letter exchange) gönderilir. Eğer aynı olayların daha sonra başka tüketiciler tarafından da tekrar oynatılması (replay) gerekiyorsa JetStream kullanın; çünkü bir akış (stream), mesajlar onaylandıktan sonra da onları tutmaya devam eder, ancak kuyruklar bunu yapmaz. JetStream kullanırken --max-deliver ayarını yapmalı ve ölü mektup yolunu $JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.> bildirimlerini kullanarak kendiniz oluşturmalısınız.

Tüketicilerimin ne kadar geride kaldığını nasıl anlarım?

Her mesaj aracısının (broker) kendine ait bir komutu vardır. RabbitMQ için rabbitmqctl list_queues name messages messages_ready messages_unacknowledged, tüketiciyi bekleyen işler ile teslim edilmiş ancak onaylanmamış (ack) işleri birbirinden ayırır. JetStream için nats consumer report <stream>, tüketici başına işlenmemiş mesajları ve onay bekleyen işlemleri gösterir. Kafka için kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group <group>, bölüm (partition) başına bir LAG sütunu yazdırır. Core NATS herhangi bir veri depolamadığı için okunacak bir birikmiş iş (backlog) yoktur; bunun yerine http://localhost:8222/varz üzerindeki slow_consumers sayacını izleyin. Bu sayaç, geride kaldığı için sunucu tarafından kapatılan bağlantıları, yani mesaj kaybını gösterir.

#nats#rabbitmq#kafka#message-queue#architecture